@algosuite/vo-mcp 0.2.0-beta.1 → 0.2.0-beta.4
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 +154 -153
- package/bin/vo-mcp +38 -33
- package/dist/autostart-cli.js +79 -2
- package/dist/autostart-cli.js.map +2 -2
- package/dist/cli.js +1020 -745
- package/dist/cli.js.map +4 -4
- package/dist/index.js +111 -96
- package/dist/index.js.map +3 -3
- package/dist/install-cli.js +88 -26
- package/dist/install-cli.js.map +3 -3
- package/dist/login-cli.js +0 -0
- package/dist/login-cli.js.map +1 -1
- package/dist/pair-cli.js +214 -0
- package/dist/pair-cli.js.map +7 -0
- package/dist/runner-cli.js +1365 -185
- package/dist/runner-cli.js.map +4 -4
- package/dist/set-key-cli.js +170 -0
- package/dist/set-key-cli.js.map +7 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -15,500 +15,222 @@ var __export = (target, all) => {
|
|
|
15
15
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
// src/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
// ../vo-arch-defaults/src/schema/rule-v1.ts
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
function parseRule(input) {
|
|
21
|
+
return ArchitecturalDefaultRuleSchema.parse(input);
|
|
22
|
+
}
|
|
23
|
+
var EvidenceMatcherKind, EvidenceMatcher, ChangeType, AppliesWhen, Reference, IsoDate, RuleId, ArchitecturalDefaultRuleSchema;
|
|
24
|
+
var init_rule_v1 = __esm({
|
|
25
|
+
"../vo-arch-defaults/src/schema/rule-v1.ts"() {
|
|
26
|
+
"use strict";
|
|
27
|
+
EvidenceMatcherKind = z.enum([
|
|
28
|
+
"regex",
|
|
29
|
+
"import-detector",
|
|
30
|
+
"package-json-field",
|
|
31
|
+
"file-size",
|
|
32
|
+
"ast-pattern",
|
|
33
|
+
"custom"
|
|
34
|
+
]);
|
|
35
|
+
EvidenceMatcher = z.object({
|
|
36
|
+
kind: EvidenceMatcherKind,
|
|
37
|
+
pattern: z.string().optional(),
|
|
38
|
+
config: z.record(z.string(), z.unknown()).optional(),
|
|
39
|
+
description: z.string().min(1)
|
|
40
|
+
}).strict().refine(
|
|
41
|
+
(m) => m.kind !== "regex" || typeof m.pattern === "string" && m.pattern.length > 0,
|
|
42
|
+
{ message: "regex matcher requires non-empty pattern" }
|
|
43
|
+
);
|
|
44
|
+
ChangeType = z.enum(["new-file", "edit", "delete", "rename", "any"]);
|
|
45
|
+
AppliesWhen = z.object({
|
|
46
|
+
stack: z.array(z.string().min(1)).optional(),
|
|
47
|
+
change_types: z.array(ChangeType).optional(),
|
|
48
|
+
file_globs: z.array(z.string().min(1)).optional(),
|
|
49
|
+
not_file_globs: z.array(z.string().min(1)).optional()
|
|
50
|
+
}).strict();
|
|
51
|
+
Reference = z.object({
|
|
52
|
+
type: z.enum(["framework-doc", "internal-doc", "pr", "incident", "external"]),
|
|
53
|
+
url: z.string().min(1),
|
|
54
|
+
description: z.string().min(1)
|
|
55
|
+
}).strict();
|
|
56
|
+
IsoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "last_verified must be ISO date YYYY-MM-DD").refine((s) => {
|
|
57
|
+
const d = /* @__PURE__ */ new Date(s + "T00:00:00Z");
|
|
58
|
+
return !Number.isNaN(d.getTime()) && d.toISOString().startsWith(s);
|
|
59
|
+
}, "last_verified must be a real calendar date");
|
|
60
|
+
RuleId = z.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/, {
|
|
61
|
+
message: "rule_id must be `<category>/<kebab-name>`"
|
|
62
|
+
});
|
|
63
|
+
ArchitecturalDefaultRuleSchema = z.object({
|
|
64
|
+
schema_version: z.literal(1),
|
|
65
|
+
rule_id: RuleId,
|
|
66
|
+
rule_version: z.number().int().positive(),
|
|
67
|
+
category: z.string().min(1),
|
|
68
|
+
severity: z.enum(["blocker", "warning", "info"]),
|
|
69
|
+
title: z.string().min(1),
|
|
70
|
+
rationale: z.string().min(1),
|
|
71
|
+
applies_when: AppliesWhen,
|
|
72
|
+
evidence_of_violation: z.array(EvidenceMatcher).min(1, {
|
|
73
|
+
message: "rule must declare at least one evidence matcher"
|
|
74
|
+
}),
|
|
75
|
+
remediation: z.string().min(1),
|
|
76
|
+
references: z.array(Reference),
|
|
77
|
+
last_verified: IsoDate,
|
|
78
|
+
tags: z.array(z.string().min(1))
|
|
79
|
+
}).strict();
|
|
80
|
+
}
|
|
26
81
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
82
|
+
|
|
83
|
+
// ../vo-arch-defaults/src/schema/override-v1.ts
|
|
84
|
+
import { z as z2 } from "zod";
|
|
85
|
+
function parseOverride(input) {
|
|
86
|
+
return TenantOverrideSchema.parse(input);
|
|
30
87
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const expiresInSec = Number(parsed.expires_in);
|
|
61
|
-
const ttlMs = Number.isFinite(expiresInSec) && expiresInSec > 0 ? expiresInSec * 1e3 : 36e5;
|
|
62
|
-
cachedToken = idToken;
|
|
63
|
-
expiresAtMs = now() + ttlMs;
|
|
64
|
-
return idToken;
|
|
65
|
-
} catch {
|
|
66
|
-
cachedToken = null;
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
88
|
+
var PartialRuleSchema, TenantOverrideSchema;
|
|
89
|
+
var init_override_v1 = __esm({
|
|
90
|
+
"../vo-arch-defaults/src/schema/override-v1.ts"() {
|
|
91
|
+
"use strict";
|
|
92
|
+
init_rule_v1();
|
|
93
|
+
PartialRuleSchema = ArchitecturalDefaultRuleSchema.partial();
|
|
94
|
+
TenantOverrideSchema = z2.object({
|
|
95
|
+
schema_version: z2.literal(1),
|
|
96
|
+
suppressed_rule_ids: z2.array(z2.string().min(1)),
|
|
97
|
+
modified_rules: z2.record(z2.string().min(1), PartialRuleSchema),
|
|
98
|
+
added_rules: z2.array(ArchitecturalDefaultRuleSchema),
|
|
99
|
+
/**
|
|
100
|
+
* Per-tenant stack override (added 2026-05-24 — audit HIGH
|
|
101
|
+
* "DEFAULT_STACK hardcoded for Nexus repo"). When set, downstream
|
|
102
|
+
* consumers (vo-mcp KB pre-filter, vo-arch-check CLI) use this list
|
|
103
|
+
* instead of their built-in default. Lets external tenants whose
|
|
104
|
+
* repo isn't `firebase+react+pnpm` get useful KB rule matches by
|
|
105
|
+
* dropping a `~/.claude/vo-arch-defaults.local.json` with their own
|
|
106
|
+
* stack identifiers — no code change required.
|
|
107
|
+
*
|
|
108
|
+
* Open string union — values are not constrained beyond non-empty
|
|
109
|
+
* strings so out-of-tree tenants can declare their own
|
|
110
|
+
* (e.g. `vercel-edge`, `next-15`, `drizzle-postgres`).
|
|
111
|
+
*
|
|
112
|
+
* Optional. Undefined / omitted preserves pre-2026-05-24 behavior
|
|
113
|
+
* (consumer falls back to its hardcoded default stack).
|
|
114
|
+
*/
|
|
115
|
+
tenant_stack: z2.array(z2.string().min(1)).optional()
|
|
116
|
+
}).strict();
|
|
69
117
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// ../vo-arch-defaults/src/schema/index.ts
|
|
121
|
+
var init_schema = __esm({
|
|
122
|
+
"../vo-arch-defaults/src/schema/index.ts"() {
|
|
123
|
+
"use strict";
|
|
124
|
+
init_rule_v1();
|
|
125
|
+
init_override_v1();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// ../vo-arch-defaults/src/storage/load-bundled.ts
|
|
130
|
+
import { readdirSync, readFileSync, statSync, existsSync } from "node:fs";
|
|
131
|
+
import { dirname, join } from "node:path";
|
|
132
|
+
import { fileURLToPath } from "node:url";
|
|
133
|
+
function resolveBundledCorpusDir() {
|
|
134
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
135
|
+
const candidates = [
|
|
136
|
+
join(here, "..", "corpus"),
|
|
137
|
+
// dist/corpus next to dist/storage
|
|
138
|
+
join(here, "..", "..", "corpus")
|
|
139
|
+
// src/corpus next to src/storage (under vitest)
|
|
140
|
+
];
|
|
141
|
+
for (const c of candidates) {
|
|
142
|
+
if (existsSync(c) && statSync(c).isDirectory()) return c;
|
|
143
|
+
}
|
|
144
|
+
throw new Error(
|
|
145
|
+
`vo-arch-defaults: could not locate bundled corpus directory. Tried: ${candidates.join(", ")}`
|
|
146
|
+
);
|
|
82
147
|
}
|
|
83
|
-
function
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
"Per-user refresh auth requires BOTH VO_USER_REFRESH_TOKEN and VO_FIREBASE_API_KEY"
|
|
92
|
-
);
|
|
148
|
+
function walkJson(dir, out) {
|
|
149
|
+
for (const entry of readdirSync(dir)) {
|
|
150
|
+
const full = join(dir, entry);
|
|
151
|
+
const st = statSync(full);
|
|
152
|
+
if (st.isDirectory()) {
|
|
153
|
+
walkJson(full, out);
|
|
154
|
+
} else if (entry.endsWith(".json")) {
|
|
155
|
+
out.push(full);
|
|
93
156
|
}
|
|
94
|
-
return createFirebaseRefreshTokenSource({
|
|
95
|
-
refreshToken,
|
|
96
|
-
apiKey,
|
|
97
|
-
...fetchFn ? { fetchFn } : {}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
if (idToken) return createStaticTokenSource(idToken, "firebase-id-token");
|
|
101
|
-
const stored = readStoredCred();
|
|
102
|
-
if (stored?.vo_credential && stored.vo_credential.trim()) {
|
|
103
|
-
return createStaticTokenSource(stored.vo_credential.trim(), "vo-credential");
|
|
104
157
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
158
|
+
}
|
|
159
|
+
function loadBundledCorpus(opts = {}) {
|
|
160
|
+
const dir = opts.corpusDir ?? resolveBundledCorpusDir();
|
|
161
|
+
const files = [];
|
|
162
|
+
walkJson(dir, files);
|
|
163
|
+
files.sort();
|
|
164
|
+
const rules = [];
|
|
165
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
166
|
+
for (const file of files) {
|
|
167
|
+
const raw = readFileSync(file, "utf8");
|
|
168
|
+
let parsed;
|
|
169
|
+
try {
|
|
170
|
+
parsed = JSON.parse(raw);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
173
|
+
throw new Error(`vo-arch-defaults: invalid JSON in ${file}: ${m}`, { cause: err });
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const rule = parseRule(parsed);
|
|
177
|
+
if (seenIds.has(rule.rule_id)) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`vo-arch-defaults: duplicate rule_id '${rule.rule_id}' (second occurrence in ${file})`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
seenIds.add(rule.rule_id);
|
|
183
|
+
rules.push(rule);
|
|
184
|
+
} catch (err) {
|
|
185
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
186
|
+
throw new Error(`vo-arch-defaults: schema validation failed for ${file}: ${m}`, { cause: err });
|
|
187
|
+
}
|
|
111
188
|
}
|
|
112
|
-
|
|
113
|
-
return null;
|
|
189
|
+
return { rules, source_paths: files };
|
|
114
190
|
}
|
|
115
|
-
var
|
|
116
|
-
|
|
117
|
-
"src/cloud/auth-token-source.ts"() {
|
|
191
|
+
var init_load_bundled = __esm({
|
|
192
|
+
"../vo-arch-defaults/src/storage/load-bundled.ts"() {
|
|
118
193
|
"use strict";
|
|
119
|
-
|
|
120
|
-
FIREBASE_TOKEN_REFERER = "https://algosuite.ai/";
|
|
121
|
-
REFRESH_SKEW_MS = 6e4;
|
|
194
|
+
init_schema();
|
|
122
195
|
}
|
|
123
196
|
});
|
|
124
197
|
|
|
125
|
-
// src/
|
|
126
|
-
import {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const mod = req("@napi-rs/keyring");
|
|
132
|
-
cached = mod && typeof mod.Entry === "function" ? mod : null;
|
|
133
|
-
} catch {
|
|
134
|
-
cached = null;
|
|
135
|
-
}
|
|
136
|
-
return cached;
|
|
137
|
-
}
|
|
138
|
-
function keychainAvailable() {
|
|
139
|
-
return loadKeyring() !== null;
|
|
198
|
+
// ../vo-arch-defaults/src/storage/load-override.ts
|
|
199
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
200
|
+
import { homedir } from "node:os";
|
|
201
|
+
import { join as join2 } from "node:path";
|
|
202
|
+
function defaultOverridePath() {
|
|
203
|
+
return join2(homedir(), ".claude", "vo-arch-defaults.local.json");
|
|
140
204
|
}
|
|
141
|
-
function
|
|
142
|
-
const
|
|
143
|
-
if (!
|
|
144
|
-
|
|
145
|
-
return new k.Entry(SERVICE, ACCOUNT).getPassword();
|
|
146
|
-
} catch {
|
|
147
|
-
return null;
|
|
205
|
+
function loadTenantOverride(opts = {}) {
|
|
206
|
+
const path3 = opts.path ?? defaultOverridePath();
|
|
207
|
+
if (!existsSync2(path3)) {
|
|
208
|
+
return { override: null, source_path: null };
|
|
148
209
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const k = loadKeyring();
|
|
152
|
-
if (!k) return false;
|
|
210
|
+
const raw = readFileSync2(path3, "utf8");
|
|
211
|
+
let parsed;
|
|
153
212
|
try {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
213
|
+
parsed = JSON.parse(raw);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
216
|
+
throw new Error(`vo-arch-defaults: invalid JSON in override ${path3}: ${m}`, { cause: err });
|
|
158
217
|
}
|
|
159
|
-
}
|
|
160
|
-
function keychainDelete() {
|
|
161
|
-
const k = loadKeyring();
|
|
162
|
-
if (!k) return false;
|
|
163
218
|
try {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
219
|
+
const override = parseOverride(parsed);
|
|
220
|
+
return { override, source_path: path3 };
|
|
221
|
+
} catch (err) {
|
|
222
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
223
|
+
throw new Error(`vo-arch-defaults: override schema validation failed for ${path3}: ${m}`, { cause: err });
|
|
167
224
|
}
|
|
168
225
|
}
|
|
169
|
-
var
|
|
170
|
-
|
|
171
|
-
"src/cloud/keychain.ts"() {
|
|
226
|
+
var init_load_override = __esm({
|
|
227
|
+
"../vo-arch-defaults/src/storage/load-override.ts"() {
|
|
172
228
|
"use strict";
|
|
173
|
-
|
|
174
|
-
ACCOUNT = "refresh-credential";
|
|
229
|
+
init_schema();
|
|
175
230
|
}
|
|
176
231
|
});
|
|
177
232
|
|
|
178
|
-
// src/cloud/credential-store.ts
|
|
179
|
-
var credential_store_exports = {};
|
|
180
|
-
__export(credential_store_exports, {
|
|
181
|
-
KEYCHAIN_LOCATION: () => KEYCHAIN_LOCATION,
|
|
182
|
-
credentialPath: () => credentialPath,
|
|
183
|
-
readStoredCredential: () => readStoredCredential,
|
|
184
|
-
writeStoredCredential: () => writeStoredCredential
|
|
185
|
-
});
|
|
186
|
-
import { homedir as homedir3 } from "node:os";
|
|
187
|
-
import { join as join5, dirname as dirname4 } from "node:path";
|
|
188
|
-
import {
|
|
189
|
-
existsSync as existsSync3,
|
|
190
|
-
mkdirSync as mkdirSync2,
|
|
191
|
-
readFileSync as readFileSync5,
|
|
192
|
-
writeFileSync as writeFileSync2,
|
|
193
|
-
chmodSync as chmodSync2,
|
|
194
|
-
rmSync
|
|
195
|
-
} from "node:fs";
|
|
196
|
-
function credentialPath(env = process.env) {
|
|
197
|
-
const override = env["VO_MCP_CREDENTIALS_PATH"]?.trim();
|
|
198
|
-
if (override) return override;
|
|
199
|
-
return join5(homedir3(), ".config", "vo-mcp", "credentials.json");
|
|
200
|
-
}
|
|
201
|
-
function keychainEnabled(env, keychain) {
|
|
202
|
-
const disabled = (env["VO_MCP_DISABLE_KEYCHAIN"] ?? "").trim().toLowerCase();
|
|
203
|
-
if (disabled === "1" || disabled === "true" || disabled === "yes") return false;
|
|
204
|
-
return keychain.available();
|
|
205
|
-
}
|
|
206
|
-
function deserialize(raw) {
|
|
207
|
-
try {
|
|
208
|
-
const parsed = JSON.parse(raw);
|
|
209
|
-
const refresh = typeof parsed.refresh_token === "string" ? parsed.refresh_token.trim() : "";
|
|
210
|
-
const apiKey = typeof parsed.api_key === "string" ? parsed.api_key.trim() : "";
|
|
211
|
-
const voCred = typeof parsed.vo_credential === "string" ? parsed.vo_credential.trim() : "";
|
|
212
|
-
if (!voCred && (!refresh || !apiKey)) return null;
|
|
213
|
-
return {
|
|
214
|
-
...refresh ? { refresh_token: refresh } : {},
|
|
215
|
-
...apiKey ? { api_key: apiKey } : {},
|
|
216
|
-
...voCred ? { vo_credential: voCred } : {},
|
|
217
|
-
...typeof parsed.vo_credential_expires_at === "string" ? { vo_credential_expires_at: parsed.vo_credential_expires_at } : {},
|
|
218
|
-
...typeof parsed.email === "string" ? { email: parsed.email } : {},
|
|
219
|
-
...typeof parsed.stored_at === "string" ? { stored_at: parsed.stored_at } : {}
|
|
220
|
-
};
|
|
221
|
-
} catch {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
function readFromFile(env) {
|
|
226
|
-
try {
|
|
227
|
-
const p = credentialPath(env);
|
|
228
|
-
if (!existsSync3(p)) return null;
|
|
229
|
-
return deserialize(readFileSync5(p, "utf8"));
|
|
230
|
-
} catch {
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
function readStoredCredential(env = process.env, keychain = realKeychain) {
|
|
235
|
-
if (keychainEnabled(env, keychain)) {
|
|
236
|
-
const raw = keychain.get();
|
|
237
|
-
const fromKeychain = raw ? deserialize(raw) : null;
|
|
238
|
-
if (fromKeychain) return fromKeychain;
|
|
239
|
-
}
|
|
240
|
-
return readFromFile(env);
|
|
241
|
-
}
|
|
242
|
-
function deleteFile(env) {
|
|
243
|
-
try {
|
|
244
|
-
rmSync(credentialPath(env), { force: true });
|
|
245
|
-
} catch {
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
function writeToFile(payload, env) {
|
|
249
|
-
const p = credentialPath(env);
|
|
250
|
-
mkdirSync2(dirname4(p), { recursive: true });
|
|
251
|
-
writeFileSync2(p, `${JSON.stringify(payload, null, 2)}
|
|
252
|
-
`, { mode: 384 });
|
|
253
|
-
try {
|
|
254
|
-
chmodSync2(p, 384);
|
|
255
|
-
} catch {
|
|
256
|
-
}
|
|
257
|
-
return p;
|
|
258
|
-
}
|
|
259
|
-
function writeStoredCredential(cred, storedAt, env = process.env, keychain = realKeychain) {
|
|
260
|
-
const payload = {
|
|
261
|
-
...cred.refresh_token ? { refresh_token: cred.refresh_token } : {},
|
|
262
|
-
...cred.api_key ? { api_key: cred.api_key } : {},
|
|
263
|
-
...cred.vo_credential ? { vo_credential: cred.vo_credential } : {},
|
|
264
|
-
...cred.vo_credential_expires_at ? { vo_credential_expires_at: cred.vo_credential_expires_at } : {},
|
|
265
|
-
...cred.email ? { email: cred.email } : {},
|
|
266
|
-
stored_at: cred.stored_at ?? storedAt
|
|
267
|
-
};
|
|
268
|
-
if (keychainEnabled(env, keychain) && keychain.set(JSON.stringify(payload))) {
|
|
269
|
-
deleteFile(env);
|
|
270
|
-
return KEYCHAIN_LOCATION;
|
|
271
|
-
}
|
|
272
|
-
const p = writeToFile(payload, env);
|
|
273
|
-
if (keychainEnabled(env, keychain)) keychain.delete();
|
|
274
|
-
return p;
|
|
275
|
-
}
|
|
276
|
-
var realKeychain, KEYCHAIN_LOCATION;
|
|
277
|
-
var init_credential_store = __esm({
|
|
278
|
-
"src/cloud/credential-store.ts"() {
|
|
279
|
-
"use strict";
|
|
280
|
-
init_keychain();
|
|
281
|
-
realKeychain = {
|
|
282
|
-
available: keychainAvailable,
|
|
283
|
-
get: keychainGet,
|
|
284
|
-
set: keychainSet,
|
|
285
|
-
delete: keychainDelete
|
|
286
|
-
};
|
|
287
|
-
KEYCHAIN_LOCATION = 'OS keychain (service "vo-mcp")';
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// src/cli.ts
|
|
292
|
-
import { homedir as homedir6, hostname } from "node:os";
|
|
293
|
-
import { join as join8 } from "node:path";
|
|
294
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
295
|
-
|
|
296
|
-
// src/server.ts
|
|
297
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
298
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
299
|
-
import {
|
|
300
|
-
CallToolRequestSchema,
|
|
301
|
-
ListToolsRequestSchema
|
|
302
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
303
|
-
|
|
304
|
-
// src/tools/common.ts
|
|
305
|
-
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
306
|
-
import { readFileSync as readFileSync4 } from "node:fs";
|
|
307
|
-
import { dirname as dirname3, join as join4 } from "node:path";
|
|
308
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
309
|
-
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
310
|
-
|
|
311
|
-
// src/logging/events-writer.ts
|
|
312
|
-
import {
|
|
313
|
-
appendFileSync,
|
|
314
|
-
chmodSync,
|
|
315
|
-
mkdirSync,
|
|
316
|
-
readdirSync as readdirSync2,
|
|
317
|
-
readFileSync as readFileSync3,
|
|
318
|
-
statSync as statSync2,
|
|
319
|
-
unlinkSync,
|
|
320
|
-
writeFileSync
|
|
321
|
-
} from "node:fs";
|
|
322
|
-
import { homedir as homedir2 } from "node:os";
|
|
323
|
-
import { basename, dirname as dirname2, join as join3 } from "node:path";
|
|
324
|
-
import { gzipSync } from "node:zlib";
|
|
325
|
-
|
|
326
|
-
// ../vo-arch-defaults/src/schema/rule-v1.ts
|
|
327
|
-
import { z } from "zod";
|
|
328
|
-
var EvidenceMatcherKind = z.enum([
|
|
329
|
-
"regex",
|
|
330
|
-
"import-detector",
|
|
331
|
-
"package-json-field",
|
|
332
|
-
"file-size",
|
|
333
|
-
"ast-pattern",
|
|
334
|
-
"custom"
|
|
335
|
-
]);
|
|
336
|
-
var EvidenceMatcher = z.object({
|
|
337
|
-
kind: EvidenceMatcherKind,
|
|
338
|
-
pattern: z.string().optional(),
|
|
339
|
-
config: z.record(z.string(), z.unknown()).optional(),
|
|
340
|
-
description: z.string().min(1)
|
|
341
|
-
}).strict().refine(
|
|
342
|
-
(m) => m.kind !== "regex" || typeof m.pattern === "string" && m.pattern.length > 0,
|
|
343
|
-
{ message: "regex matcher requires non-empty pattern" }
|
|
344
|
-
);
|
|
345
|
-
var ChangeType = z.enum(["new-file", "edit", "delete", "rename", "any"]);
|
|
346
|
-
var AppliesWhen = z.object({
|
|
347
|
-
stack: z.array(z.string().min(1)).optional(),
|
|
348
|
-
change_types: z.array(ChangeType).optional(),
|
|
349
|
-
file_globs: z.array(z.string().min(1)).optional(),
|
|
350
|
-
not_file_globs: z.array(z.string().min(1)).optional()
|
|
351
|
-
}).strict();
|
|
352
|
-
var Reference = z.object({
|
|
353
|
-
type: z.enum(["framework-doc", "internal-doc", "pr", "incident", "external"]),
|
|
354
|
-
url: z.string().min(1),
|
|
355
|
-
description: z.string().min(1)
|
|
356
|
-
}).strict();
|
|
357
|
-
var IsoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "last_verified must be ISO date YYYY-MM-DD").refine((s) => {
|
|
358
|
-
const d = /* @__PURE__ */ new Date(s + "T00:00:00Z");
|
|
359
|
-
return !Number.isNaN(d.getTime()) && d.toISOString().startsWith(s);
|
|
360
|
-
}, "last_verified must be a real calendar date");
|
|
361
|
-
var RuleId = z.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/, {
|
|
362
|
-
message: "rule_id must be `<category>/<kebab-name>`"
|
|
363
|
-
});
|
|
364
|
-
var ArchitecturalDefaultRuleSchema = z.object({
|
|
365
|
-
schema_version: z.literal(1),
|
|
366
|
-
rule_id: RuleId,
|
|
367
|
-
rule_version: z.number().int().positive(),
|
|
368
|
-
category: z.string().min(1),
|
|
369
|
-
severity: z.enum(["blocker", "warning", "info"]),
|
|
370
|
-
title: z.string().min(1),
|
|
371
|
-
rationale: z.string().min(1),
|
|
372
|
-
applies_when: AppliesWhen,
|
|
373
|
-
evidence_of_violation: z.array(EvidenceMatcher).min(1, {
|
|
374
|
-
message: "rule must declare at least one evidence matcher"
|
|
375
|
-
}),
|
|
376
|
-
remediation: z.string().min(1),
|
|
377
|
-
references: z.array(Reference),
|
|
378
|
-
last_verified: IsoDate,
|
|
379
|
-
tags: z.array(z.string().min(1))
|
|
380
|
-
}).strict();
|
|
381
|
-
function parseRule(input) {
|
|
382
|
-
return ArchitecturalDefaultRuleSchema.parse(input);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// ../vo-arch-defaults/src/schema/override-v1.ts
|
|
386
|
-
import { z as z2 } from "zod";
|
|
387
|
-
var PartialRuleSchema = ArchitecturalDefaultRuleSchema.partial();
|
|
388
|
-
var TenantOverrideSchema = z2.object({
|
|
389
|
-
schema_version: z2.literal(1),
|
|
390
|
-
suppressed_rule_ids: z2.array(z2.string().min(1)),
|
|
391
|
-
modified_rules: z2.record(z2.string().min(1), PartialRuleSchema),
|
|
392
|
-
added_rules: z2.array(ArchitecturalDefaultRuleSchema),
|
|
393
|
-
/**
|
|
394
|
-
* Per-tenant stack override (added 2026-05-24 — audit HIGH
|
|
395
|
-
* "DEFAULT_STACK hardcoded for Nexus repo"). When set, downstream
|
|
396
|
-
* consumers (vo-mcp KB pre-filter, vo-arch-check CLI) use this list
|
|
397
|
-
* instead of their built-in default. Lets external tenants whose
|
|
398
|
-
* repo isn't `firebase+react+pnpm` get useful KB rule matches by
|
|
399
|
-
* dropping a `~/.claude/vo-arch-defaults.local.json` with their own
|
|
400
|
-
* stack identifiers — no code change required.
|
|
401
|
-
*
|
|
402
|
-
* Open string union — values are not constrained beyond non-empty
|
|
403
|
-
* strings so out-of-tree tenants can declare their own
|
|
404
|
-
* (e.g. `vercel-edge`, `next-15`, `drizzle-postgres`).
|
|
405
|
-
*
|
|
406
|
-
* Optional. Undefined / omitted preserves pre-2026-05-24 behavior
|
|
407
|
-
* (consumer falls back to its hardcoded default stack).
|
|
408
|
-
*/
|
|
409
|
-
tenant_stack: z2.array(z2.string().min(1)).optional()
|
|
410
|
-
}).strict();
|
|
411
|
-
function parseOverride(input) {
|
|
412
|
-
return TenantOverrideSchema.parse(input);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// ../vo-arch-defaults/src/storage/load-bundled.ts
|
|
416
|
-
import { readdirSync, readFileSync, statSync, existsSync } from "node:fs";
|
|
417
|
-
import { dirname, join } from "node:path";
|
|
418
|
-
import { fileURLToPath } from "node:url";
|
|
419
|
-
function resolveBundledCorpusDir() {
|
|
420
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
421
|
-
const candidates = [
|
|
422
|
-
join(here, "..", "corpus"),
|
|
423
|
-
// dist/corpus next to dist/storage
|
|
424
|
-
join(here, "..", "..", "corpus")
|
|
425
|
-
// src/corpus next to src/storage (under vitest)
|
|
426
|
-
];
|
|
427
|
-
for (const c of candidates) {
|
|
428
|
-
if (existsSync(c) && statSync(c).isDirectory()) return c;
|
|
429
|
-
}
|
|
430
|
-
throw new Error(
|
|
431
|
-
`vo-arch-defaults: could not locate bundled corpus directory. Tried: ${candidates.join(", ")}`
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
function walkJson(dir, out) {
|
|
435
|
-
for (const entry of readdirSync(dir)) {
|
|
436
|
-
const full = join(dir, entry);
|
|
437
|
-
const st = statSync(full);
|
|
438
|
-
if (st.isDirectory()) {
|
|
439
|
-
walkJson(full, out);
|
|
440
|
-
} else if (entry.endsWith(".json")) {
|
|
441
|
-
out.push(full);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
function loadBundledCorpus(opts = {}) {
|
|
446
|
-
const dir = opts.corpusDir ?? resolveBundledCorpusDir();
|
|
447
|
-
const files = [];
|
|
448
|
-
walkJson(dir, files);
|
|
449
|
-
files.sort();
|
|
450
|
-
const rules = [];
|
|
451
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
452
|
-
for (const file of files) {
|
|
453
|
-
const raw = readFileSync(file, "utf8");
|
|
454
|
-
let parsed;
|
|
455
|
-
try {
|
|
456
|
-
parsed = JSON.parse(raw);
|
|
457
|
-
} catch (err) {
|
|
458
|
-
const m = err instanceof Error ? err.message : String(err);
|
|
459
|
-
throw new Error(`vo-arch-defaults: invalid JSON in ${file}: ${m}`, { cause: err });
|
|
460
|
-
}
|
|
461
|
-
try {
|
|
462
|
-
const rule = parseRule(parsed);
|
|
463
|
-
if (seenIds.has(rule.rule_id)) {
|
|
464
|
-
throw new Error(
|
|
465
|
-
`vo-arch-defaults: duplicate rule_id '${rule.rule_id}' (second occurrence in ${file})`
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
seenIds.add(rule.rule_id);
|
|
469
|
-
rules.push(rule);
|
|
470
|
-
} catch (err) {
|
|
471
|
-
const m = err instanceof Error ? err.message : String(err);
|
|
472
|
-
throw new Error(`vo-arch-defaults: schema validation failed for ${file}: ${m}`, { cause: err });
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
return { rules, source_paths: files };
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// ../vo-arch-defaults/src/storage/load-override.ts
|
|
479
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
480
|
-
import { homedir } from "node:os";
|
|
481
|
-
import { join as join2 } from "node:path";
|
|
482
|
-
function defaultOverridePath() {
|
|
483
|
-
return join2(homedir(), ".claude", "vo-arch-defaults.local.json");
|
|
484
|
-
}
|
|
485
|
-
function loadTenantOverride(opts = {}) {
|
|
486
|
-
const path3 = opts.path ?? defaultOverridePath();
|
|
487
|
-
if (!existsSync2(path3)) {
|
|
488
|
-
return { override: null, source_path: null };
|
|
489
|
-
}
|
|
490
|
-
const raw = readFileSync2(path3, "utf8");
|
|
491
|
-
let parsed;
|
|
492
|
-
try {
|
|
493
|
-
parsed = JSON.parse(raw);
|
|
494
|
-
} catch (err) {
|
|
495
|
-
const m = err instanceof Error ? err.message : String(err);
|
|
496
|
-
throw new Error(`vo-arch-defaults: invalid JSON in override ${path3}: ${m}`, { cause: err });
|
|
497
|
-
}
|
|
498
|
-
try {
|
|
499
|
-
const override = parseOverride(parsed);
|
|
500
|
-
return { override, source_path: path3 };
|
|
501
|
-
} catch (err) {
|
|
502
|
-
const m = err instanceof Error ? err.message : String(err);
|
|
503
|
-
throw new Error(`vo-arch-defaults: override schema validation failed for ${path3}: ${m}`, { cause: err });
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
233
|
// ../vo-arch-defaults/src/storage/merge.ts
|
|
508
|
-
var IMMUTABLE_FIELDS = [
|
|
509
|
-
"schema_version",
|
|
510
|
-
"rule_id"
|
|
511
|
-
];
|
|
512
234
|
function applyModification(base, patch) {
|
|
513
235
|
const cleanPatch = { ...patch };
|
|
514
236
|
for (const k of IMMUTABLE_FIELDS) {
|
|
@@ -560,6 +282,16 @@ function mergeCorpusWithOverride(bundled, override) {
|
|
|
560
282
|
added_count: addedCount
|
|
561
283
|
};
|
|
562
284
|
}
|
|
285
|
+
var IMMUTABLE_FIELDS;
|
|
286
|
+
var init_merge = __esm({
|
|
287
|
+
"../vo-arch-defaults/src/storage/merge.ts"() {
|
|
288
|
+
"use strict";
|
|
289
|
+
IMMUTABLE_FIELDS = [
|
|
290
|
+
"schema_version",
|
|
291
|
+
"rule_id"
|
|
292
|
+
];
|
|
293
|
+
}
|
|
294
|
+
});
|
|
563
295
|
|
|
564
296
|
// ../vo-arch-defaults/src/query/applies-to-stack.ts
|
|
565
297
|
function appliesToStack(rule, stacks) {
|
|
@@ -572,6 +304,11 @@ function appliesToStack(rule, stacks) {
|
|
|
572
304
|
}
|
|
573
305
|
return false;
|
|
574
306
|
}
|
|
307
|
+
var init_applies_to_stack = __esm({
|
|
308
|
+
"../vo-arch-defaults/src/query/applies-to-stack.ts"() {
|
|
309
|
+
"use strict";
|
|
310
|
+
}
|
|
311
|
+
});
|
|
575
312
|
|
|
576
313
|
// ../vo-arch-defaults/src/query/applies-to-change.ts
|
|
577
314
|
function appliesToChangeType(rule, changeType) {
|
|
@@ -581,9 +318,13 @@ function appliesToChangeType(rule, changeType) {
|
|
|
581
318
|
if (changeType === "any") return true;
|
|
582
319
|
return required.includes(changeType);
|
|
583
320
|
}
|
|
321
|
+
var init_applies_to_change = __esm({
|
|
322
|
+
"../vo-arch-defaults/src/query/applies-to-change.ts"() {
|
|
323
|
+
"use strict";
|
|
324
|
+
}
|
|
325
|
+
});
|
|
584
326
|
|
|
585
327
|
// ../vo-arch-defaults/src/query/glob.ts
|
|
586
|
-
var REGEX_META = /[.+^${}()|[\]\\]/g;
|
|
587
328
|
function globToRegExp(glob) {
|
|
588
329
|
let out = "";
|
|
589
330
|
let i = 0;
|
|
@@ -636,6 +377,13 @@ function matchesAnyGlob(path3, globs) {
|
|
|
636
377
|
}
|
|
637
378
|
return false;
|
|
638
379
|
}
|
|
380
|
+
var REGEX_META;
|
|
381
|
+
var init_glob = __esm({
|
|
382
|
+
"../vo-arch-defaults/src/query/glob.ts"() {
|
|
383
|
+
"use strict";
|
|
384
|
+
REGEX_META = /[.+^${}()|[\]\\]/g;
|
|
385
|
+
}
|
|
386
|
+
});
|
|
639
387
|
|
|
640
388
|
// ../vo-arch-defaults/src/query/applies-to-files.ts
|
|
641
389
|
function appliesToFiles(rule, filePaths) {
|
|
@@ -656,8 +404,14 @@ function appliesToFiles(rule, filePaths) {
|
|
|
656
404
|
}
|
|
657
405
|
return false;
|
|
658
406
|
}
|
|
659
|
-
|
|
660
|
-
|
|
407
|
+
var init_applies_to_files = __esm({
|
|
408
|
+
"../vo-arch-defaults/src/query/applies-to-files.ts"() {
|
|
409
|
+
"use strict";
|
|
410
|
+
init_glob();
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// ../vo-arch-defaults/src/analyze/diff-parser.ts
|
|
661
415
|
function stripPathPrefix(p) {
|
|
662
416
|
if (p.startsWith("a/") || p.startsWith("b/")) return p.slice(2);
|
|
663
417
|
return p;
|
|
@@ -727,9 +481,13 @@ function parseUnifiedDiff(text) {
|
|
|
727
481
|
}
|
|
728
482
|
return { files };
|
|
729
483
|
}
|
|
484
|
+
var init_diff_parser = __esm({
|
|
485
|
+
"../vo-arch-defaults/src/analyze/diff-parser.ts"() {
|
|
486
|
+
"use strict";
|
|
487
|
+
}
|
|
488
|
+
});
|
|
730
489
|
|
|
731
490
|
// ../vo-arch-defaults/src/analyze/evidence-matchers/regex-matcher.ts
|
|
732
|
-
var MAX_EXCERPT = 200;
|
|
733
491
|
function sanitizeLine(s) {
|
|
734
492
|
let out = s.replace(/^\s+/, "");
|
|
735
493
|
if (out.length > MAX_EXCERPT) out = out.slice(0, MAX_EXCERPT) + "\u2026";
|
|
@@ -757,12 +515,15 @@ function runRegexMatcher(matcher, file) {
|
|
|
757
515
|
}
|
|
758
516
|
return hits;
|
|
759
517
|
}
|
|
518
|
+
var MAX_EXCERPT;
|
|
519
|
+
var init_regex_matcher = __esm({
|
|
520
|
+
"../vo-arch-defaults/src/analyze/evidence-matchers/regex-matcher.ts"() {
|
|
521
|
+
"use strict";
|
|
522
|
+
MAX_EXCERPT = 200;
|
|
523
|
+
}
|
|
524
|
+
});
|
|
760
525
|
|
|
761
526
|
// ../vo-arch-defaults/src/analyze/evidence-matchers/import-detector.ts
|
|
762
|
-
var STATIC_IMPORT = /import\s+(?:[^'"\n]{0,200}?from\s+)?(['"])([^'"]+)\1/;
|
|
763
|
-
var DYNAMIC_IMPORT = /import\(\s*(['"])([^'"]+)\1\s*\)/;
|
|
764
|
-
var REQUIRE_CALL = /require\(\s*(['"])([^'"]+)\1\s*\)/;
|
|
765
|
-
var MAX_EXCERPT2 = 200;
|
|
766
527
|
function sanitize(s) {
|
|
767
528
|
let out = s.replace(/^\s+/, "");
|
|
768
529
|
if (out.length > MAX_EXCERPT2) out = out.slice(0, MAX_EXCERPT2) + "\u2026";
|
|
@@ -807,6 +568,16 @@ function runImportDetector(matcher, file) {
|
|
|
807
568
|
}
|
|
808
569
|
return hits;
|
|
809
570
|
}
|
|
571
|
+
var STATIC_IMPORT, DYNAMIC_IMPORT, REQUIRE_CALL, MAX_EXCERPT2;
|
|
572
|
+
var init_import_detector = __esm({
|
|
573
|
+
"../vo-arch-defaults/src/analyze/evidence-matchers/import-detector.ts"() {
|
|
574
|
+
"use strict";
|
|
575
|
+
STATIC_IMPORT = /import\s+(?:[^'"\n]{0,200}?from\s+)?(['"])([^'"]+)\1/;
|
|
576
|
+
DYNAMIC_IMPORT = /import\(\s*(['"])([^'"]+)\1\s*\)/;
|
|
577
|
+
REQUIRE_CALL = /require\(\s*(['"])([^'"]+)\1\s*\)/;
|
|
578
|
+
MAX_EXCERPT2 = 200;
|
|
579
|
+
}
|
|
580
|
+
});
|
|
810
581
|
|
|
811
582
|
// ../vo-arch-defaults/src/analyze/evidence-matchers/file-size.ts
|
|
812
583
|
function runFileSizeMatcher(matcher, file) {
|
|
@@ -827,6 +598,11 @@ function runFileSizeMatcher(matcher, file) {
|
|
|
827
598
|
}
|
|
828
599
|
];
|
|
829
600
|
}
|
|
601
|
+
var init_file_size = __esm({
|
|
602
|
+
"../vo-arch-defaults/src/analyze/evidence-matchers/file-size.ts"() {
|
|
603
|
+
"use strict";
|
|
604
|
+
}
|
|
605
|
+
});
|
|
830
606
|
|
|
831
607
|
// ../vo-arch-defaults/src/analyze/evidence-matchers/package-json.ts
|
|
832
608
|
function valueMatches(value, cfg) {
|
|
@@ -867,6 +643,11 @@ function runPackageJsonMatcher(matcher, file) {
|
|
|
867
643
|
}
|
|
868
644
|
return hits;
|
|
869
645
|
}
|
|
646
|
+
var init_package_json = __esm({
|
|
647
|
+
"../vo-arch-defaults/src/analyze/evidence-matchers/package-json.ts"() {
|
|
648
|
+
"use strict";
|
|
649
|
+
}
|
|
650
|
+
});
|
|
870
651
|
|
|
871
652
|
// ../vo-arch-defaults/src/analyze/evidence-matchers/index.ts
|
|
872
653
|
function runEvidenceMatcher(matcher, file) {
|
|
@@ -886,9 +667,17 @@ function runEvidenceMatcher(matcher, file) {
|
|
|
886
667
|
return [];
|
|
887
668
|
}
|
|
888
669
|
}
|
|
670
|
+
var init_evidence_matchers = __esm({
|
|
671
|
+
"../vo-arch-defaults/src/analyze/evidence-matchers/index.ts"() {
|
|
672
|
+
"use strict";
|
|
673
|
+
init_regex_matcher();
|
|
674
|
+
init_import_detector();
|
|
675
|
+
init_file_size();
|
|
676
|
+
init_package_json();
|
|
677
|
+
}
|
|
678
|
+
});
|
|
889
679
|
|
|
890
680
|
// ../vo-arch-defaults/src/query/staleness.ts
|
|
891
|
-
var DEFAULT_STALENESS_THRESHOLD_DAYS = 365;
|
|
892
681
|
function computeStaleRules(rules, opts = {}) {
|
|
893
682
|
const threshold = opts.thresholdDays ?? DEFAULT_STALENESS_THRESHOLD_DAYS;
|
|
894
683
|
if (!Number.isFinite(threshold)) return [];
|
|
@@ -912,6 +701,13 @@ function computeStaleRules(rules, opts = {}) {
|
|
|
912
701
|
out.sort((a, b) => b.days_stale - a.days_stale);
|
|
913
702
|
return out;
|
|
914
703
|
}
|
|
704
|
+
var DEFAULT_STALENESS_THRESHOLD_DAYS;
|
|
705
|
+
var init_staleness = __esm({
|
|
706
|
+
"../vo-arch-defaults/src/query/staleness.ts"() {
|
|
707
|
+
"use strict";
|
|
708
|
+
DEFAULT_STALENESS_THRESHOLD_DAYS = 365;
|
|
709
|
+
}
|
|
710
|
+
});
|
|
915
711
|
|
|
916
712
|
// ../vo-arch-defaults/src/query/run-query.ts
|
|
917
713
|
function findApplicableRules(input, opts = {}) {
|
|
@@ -970,9 +766,28 @@ function findApplicableRules(input, opts = {}) {
|
|
|
970
766
|
stale_rules
|
|
971
767
|
};
|
|
972
768
|
}
|
|
769
|
+
var init_run_query = __esm({
|
|
770
|
+
"../vo-arch-defaults/src/query/run-query.ts"() {
|
|
771
|
+
"use strict";
|
|
772
|
+
init_load_bundled();
|
|
773
|
+
init_load_override();
|
|
774
|
+
init_merge();
|
|
775
|
+
init_applies_to_stack();
|
|
776
|
+
init_applies_to_change();
|
|
777
|
+
init_applies_to_files();
|
|
778
|
+
init_diff_parser();
|
|
779
|
+
init_evidence_matchers();
|
|
780
|
+
init_staleness();
|
|
781
|
+
}
|
|
782
|
+
});
|
|
973
783
|
|
|
974
784
|
// ../vo-arch-defaults/src/query/corpus-version.ts
|
|
975
785
|
import { createHash } from "node:crypto";
|
|
786
|
+
var init_corpus_version = __esm({
|
|
787
|
+
"../vo-arch-defaults/src/query/corpus-version.ts"() {
|
|
788
|
+
"use strict";
|
|
789
|
+
}
|
|
790
|
+
});
|
|
976
791
|
|
|
977
792
|
// ../vo-arch-defaults/src/query/resolve-stack.ts
|
|
978
793
|
function resolveStack(override, fallback) {
|
|
@@ -981,9 +796,13 @@ function resolveStack(override, fallback) {
|
|
|
981
796
|
}
|
|
982
797
|
return fallback;
|
|
983
798
|
}
|
|
799
|
+
var init_resolve_stack = __esm({
|
|
800
|
+
"../vo-arch-defaults/src/query/resolve-stack.ts"() {
|
|
801
|
+
"use strict";
|
|
802
|
+
}
|
|
803
|
+
});
|
|
984
804
|
|
|
985
805
|
// ../vo-arch-defaults/src/pii-sanitize.ts
|
|
986
|
-
var SANITIZE_DEFAULT_MAX_LEN = 200;
|
|
987
806
|
function sanitizeExcerpt(raw, maxLen = SANITIZE_DEFAULT_MAX_LEN) {
|
|
988
807
|
let s = raw;
|
|
989
808
|
s = s.replace(/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}/g, "[REDACTED_JWT]");
|
|
@@ -999,10 +818,49 @@ function sanitizeExcerpt(raw, maxLen = SANITIZE_DEFAULT_MAX_LEN) {
|
|
|
999
818
|
if (s.length > maxLen) s = s.slice(0, maxLen) + "\u2026";
|
|
1000
819
|
return s;
|
|
1001
820
|
}
|
|
821
|
+
var SANITIZE_DEFAULT_MAX_LEN;
|
|
822
|
+
var init_pii_sanitize = __esm({
|
|
823
|
+
"../vo-arch-defaults/src/pii-sanitize.ts"() {
|
|
824
|
+
"use strict";
|
|
825
|
+
SANITIZE_DEFAULT_MAX_LEN = 200;
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
// ../vo-arch-defaults/src/index.ts
|
|
830
|
+
var init_src = __esm({
|
|
831
|
+
"../vo-arch-defaults/src/index.ts"() {
|
|
832
|
+
init_schema();
|
|
833
|
+
init_load_bundled();
|
|
834
|
+
init_load_override();
|
|
835
|
+
init_merge();
|
|
836
|
+
init_applies_to_stack();
|
|
837
|
+
init_applies_to_change();
|
|
838
|
+
init_applies_to_files();
|
|
839
|
+
init_run_query();
|
|
840
|
+
init_glob();
|
|
841
|
+
init_corpus_version();
|
|
842
|
+
init_resolve_stack();
|
|
843
|
+
init_staleness();
|
|
844
|
+
init_diff_parser();
|
|
845
|
+
init_evidence_matchers();
|
|
846
|
+
init_pii_sanitize();
|
|
847
|
+
}
|
|
848
|
+
});
|
|
1002
849
|
|
|
1003
850
|
// src/logging/events-writer.ts
|
|
1004
|
-
|
|
1005
|
-
|
|
851
|
+
import {
|
|
852
|
+
appendFileSync,
|
|
853
|
+
chmodSync,
|
|
854
|
+
mkdirSync,
|
|
855
|
+
readdirSync as readdirSync2,
|
|
856
|
+
readFileSync as readFileSync3,
|
|
857
|
+
statSync as statSync2,
|
|
858
|
+
unlinkSync,
|
|
859
|
+
writeFileSync
|
|
860
|
+
} from "node:fs";
|
|
861
|
+
import { homedir as homedir2 } from "node:os";
|
|
862
|
+
import { basename, dirname as dirname2, join as join3 } from "node:path";
|
|
863
|
+
import { gzipSync } from "node:zlib";
|
|
1006
864
|
function defaultEventsPath() {
|
|
1007
865
|
const envPath = process.env["VO_MCP_EVENTS_PATH"];
|
|
1008
866
|
if (envPath && envPath.length > 0) return envPath;
|
|
@@ -1109,19 +967,38 @@ function createFileEventsWriter(opts = {}) {
|
|
|
1109
967
|
}
|
|
1110
968
|
};
|
|
1111
969
|
}
|
|
970
|
+
var DEFAULT_EVENTS_MAX_BYTES, DEFAULT_EVENTS_KEEP_ROTATED;
|
|
971
|
+
var init_events_writer = __esm({
|
|
972
|
+
"src/logging/events-writer.ts"() {
|
|
973
|
+
"use strict";
|
|
974
|
+
init_src();
|
|
975
|
+
DEFAULT_EVENTS_MAX_BYTES = 50 * 1024 * 1024;
|
|
976
|
+
DEFAULT_EVENTS_KEEP_ROTATED = 10;
|
|
977
|
+
}
|
|
978
|
+
});
|
|
1112
979
|
|
|
1113
980
|
// src/tools/common.ts
|
|
981
|
+
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
982
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
983
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
984
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
985
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
1114
986
|
function readVoMcpVersion() {
|
|
1115
987
|
try {
|
|
1116
988
|
const here = dirname3(fileURLToPath2(import.meta.url));
|
|
1117
|
-
const
|
|
1118
|
-
|
|
1119
|
-
|
|
989
|
+
for (const rel of ["..", ["..", ".."], ["..", "..", ".."]]) {
|
|
990
|
+
try {
|
|
991
|
+
const segs = Array.isArray(rel) ? rel : [rel];
|
|
992
|
+
const pkg = JSON.parse(readFileSync4(join4(here, ...segs, "package.json"), "utf8"));
|
|
993
|
+
if (pkg.name === "@algosuite/vo-mcp" && typeof pkg.version === "string") return pkg.version;
|
|
994
|
+
} catch {
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
return "0.0.0-unknown";
|
|
1120
998
|
} catch {
|
|
1121
999
|
return "0.0.0-unknown";
|
|
1122
1000
|
}
|
|
1123
1001
|
}
|
|
1124
|
-
var VO_MCP_VERSION = readVoMcpVersion();
|
|
1125
1002
|
function bytesOf(s) {
|
|
1126
1003
|
return Buffer.byteLength(s, "utf8");
|
|
1127
1004
|
}
|
|
@@ -1209,6 +1086,521 @@ function toEventSynthesizedVerdict(src) {
|
|
|
1209
1086
|
reasoning_excerpt: sanitizeExcerpt(src.reasoning_excerpt)
|
|
1210
1087
|
};
|
|
1211
1088
|
}
|
|
1089
|
+
var VO_MCP_VERSION;
|
|
1090
|
+
var init_common = __esm({
|
|
1091
|
+
"src/tools/common.ts"() {
|
|
1092
|
+
"use strict";
|
|
1093
|
+
init_events_writer();
|
|
1094
|
+
VO_MCP_VERSION = readVoMcpVersion();
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
// src/cloud/auth-token-source.ts
|
|
1099
|
+
var auth_token_source_exports = {};
|
|
1100
|
+
__export(auth_token_source_exports, {
|
|
1101
|
+
FIREBASE_SECURETOKEN_URL: () => FIREBASE_SECURETOKEN_URL,
|
|
1102
|
+
FIREBASE_TOKEN_REFERER: () => FIREBASE_TOKEN_REFERER,
|
|
1103
|
+
createAuthTokenSourceFromEnv: () => createAuthTokenSourceFromEnv,
|
|
1104
|
+
createFirebaseRefreshTokenSource: () => createFirebaseRefreshTokenSource,
|
|
1105
|
+
createStaticTokenSource: () => createStaticTokenSource
|
|
1106
|
+
});
|
|
1107
|
+
function createStaticTokenSource(token, kind = "admin-token") {
|
|
1108
|
+
const value = token.trim();
|
|
1109
|
+
return { kind, getToken: async () => value.length > 0 ? value : null };
|
|
1110
|
+
}
|
|
1111
|
+
function createFirebaseRefreshTokenSource(opts) {
|
|
1112
|
+
const refreshToken = opts.refreshToken.trim();
|
|
1113
|
+
const apiKey = opts.apiKey.trim();
|
|
1114
|
+
const now = opts.now ?? (() => Date.now());
|
|
1115
|
+
const fetchFn = opts.fetchFn ?? globalThis.fetch;
|
|
1116
|
+
let cachedToken = null;
|
|
1117
|
+
let expiresAtMs = 0;
|
|
1118
|
+
let inFlight = null;
|
|
1119
|
+
async function refresh() {
|
|
1120
|
+
try {
|
|
1121
|
+
const res = await fetchFn(`${FIREBASE_SECURETOKEN_URL}?key=${encodeURIComponent(apiKey)}`, {
|
|
1122
|
+
method: "POST",
|
|
1123
|
+
headers: {
|
|
1124
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
1125
|
+
referer: FIREBASE_TOKEN_REFERER
|
|
1126
|
+
},
|
|
1127
|
+
body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(refreshToken)}`
|
|
1128
|
+
});
|
|
1129
|
+
const text = await res.text();
|
|
1130
|
+
if (res.status < 200 || res.status >= 300) {
|
|
1131
|
+
cachedToken = null;
|
|
1132
|
+
return null;
|
|
1133
|
+
}
|
|
1134
|
+
const parsed = JSON.parse(text);
|
|
1135
|
+
const idToken = typeof parsed.id_token === "string" ? parsed.id_token : "";
|
|
1136
|
+
if (!idToken) {
|
|
1137
|
+
cachedToken = null;
|
|
1138
|
+
return null;
|
|
1139
|
+
}
|
|
1140
|
+
const expiresInSec = Number(parsed.expires_in);
|
|
1141
|
+
const ttlMs = Number.isFinite(expiresInSec) && expiresInSec > 0 ? expiresInSec * 1e3 : 36e5;
|
|
1142
|
+
cachedToken = idToken;
|
|
1143
|
+
expiresAtMs = now() + ttlMs;
|
|
1144
|
+
return idToken;
|
|
1145
|
+
} catch {
|
|
1146
|
+
cachedToken = null;
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
return {
|
|
1151
|
+
kind: "firebase-refresh",
|
|
1152
|
+
async getToken() {
|
|
1153
|
+
if (cachedToken && now() < expiresAtMs - REFRESH_SKEW_MS) return cachedToken;
|
|
1154
|
+
if (!inFlight) {
|
|
1155
|
+
inFlight = refresh().finally(() => {
|
|
1156
|
+
inFlight = null;
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
return inFlight;
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
function createAuthTokenSourceFromEnv(env = process.env, fetchFn, readStoredCred = () => null) {
|
|
1164
|
+
const refreshToken = env["VO_USER_REFRESH_TOKEN"]?.trim();
|
|
1165
|
+
const apiKey = env["VO_FIREBASE_API_KEY"]?.trim();
|
|
1166
|
+
const idToken = env["VO_USER_ID_TOKEN"]?.trim();
|
|
1167
|
+
const adminToken = env["VO_CONTROL_PLANE_ADMIN_TOKEN"]?.trim();
|
|
1168
|
+
if (refreshToken || apiKey) {
|
|
1169
|
+
if (!refreshToken || !apiKey) {
|
|
1170
|
+
throw new Error(
|
|
1171
|
+
"Per-user refresh auth requires BOTH VO_USER_REFRESH_TOKEN and VO_FIREBASE_API_KEY"
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
return createFirebaseRefreshTokenSource({
|
|
1175
|
+
refreshToken,
|
|
1176
|
+
apiKey,
|
|
1177
|
+
...fetchFn ? { fetchFn } : {}
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
if (idToken) return createStaticTokenSource(idToken, "firebase-id-token");
|
|
1181
|
+
const stored = readStoredCred();
|
|
1182
|
+
if (stored?.vo_credential && stored.vo_credential.trim()) {
|
|
1183
|
+
return createStaticTokenSource(stored.vo_credential.trim(), "vo-credential");
|
|
1184
|
+
}
|
|
1185
|
+
if (stored && stored.refresh_token?.trim() && stored.api_key?.trim()) {
|
|
1186
|
+
return createFirebaseRefreshTokenSource({
|
|
1187
|
+
refreshToken: stored.refresh_token.trim(),
|
|
1188
|
+
apiKey: stored.api_key.trim(),
|
|
1189
|
+
...fetchFn ? { fetchFn } : {}
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
if (adminToken) return createStaticTokenSource(adminToken, "admin-token");
|
|
1193
|
+
return null;
|
|
1194
|
+
}
|
|
1195
|
+
var FIREBASE_SECURETOKEN_URL, FIREBASE_TOKEN_REFERER, REFRESH_SKEW_MS;
|
|
1196
|
+
var init_auth_token_source = __esm({
|
|
1197
|
+
"src/cloud/auth-token-source.ts"() {
|
|
1198
|
+
"use strict";
|
|
1199
|
+
FIREBASE_SECURETOKEN_URL = "https://securetoken.googleapis.com/v1/token";
|
|
1200
|
+
FIREBASE_TOKEN_REFERER = "https://algosuite.ai/";
|
|
1201
|
+
REFRESH_SKEW_MS = 6e4;
|
|
1202
|
+
}
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
// src/cloud/keychain.ts
|
|
1206
|
+
import { createRequire } from "node:module";
|
|
1207
|
+
function loadKeyring() {
|
|
1208
|
+
if (cached !== void 0) return cached;
|
|
1209
|
+
try {
|
|
1210
|
+
const req = createRequire(import.meta.url);
|
|
1211
|
+
const mod = req("@napi-rs/keyring");
|
|
1212
|
+
cached = mod && typeof mod.Entry === "function" ? mod : null;
|
|
1213
|
+
} catch {
|
|
1214
|
+
cached = null;
|
|
1215
|
+
}
|
|
1216
|
+
return cached;
|
|
1217
|
+
}
|
|
1218
|
+
function keychainAvailable() {
|
|
1219
|
+
return loadKeyring() !== null;
|
|
1220
|
+
}
|
|
1221
|
+
function keychainGet() {
|
|
1222
|
+
const k = loadKeyring();
|
|
1223
|
+
if (!k) return null;
|
|
1224
|
+
try {
|
|
1225
|
+
return new k.Entry(SERVICE, ACCOUNT).getPassword();
|
|
1226
|
+
} catch {
|
|
1227
|
+
return null;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
function keychainSet(secret) {
|
|
1231
|
+
const k = loadKeyring();
|
|
1232
|
+
if (!k) return false;
|
|
1233
|
+
try {
|
|
1234
|
+
new k.Entry(SERVICE, ACCOUNT).setPassword(secret);
|
|
1235
|
+
return true;
|
|
1236
|
+
} catch {
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
function keychainDelete() {
|
|
1241
|
+
const k = loadKeyring();
|
|
1242
|
+
if (!k) return false;
|
|
1243
|
+
try {
|
|
1244
|
+
return new k.Entry(SERVICE, ACCOUNT).deletePassword();
|
|
1245
|
+
} catch {
|
|
1246
|
+
return false;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
var SERVICE, ACCOUNT, cached;
|
|
1250
|
+
var init_keychain = __esm({
|
|
1251
|
+
"src/cloud/keychain.ts"() {
|
|
1252
|
+
"use strict";
|
|
1253
|
+
SERVICE = "vo-mcp";
|
|
1254
|
+
ACCOUNT = "refresh-credential";
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
// src/cloud/credential-store.ts
|
|
1259
|
+
var credential_store_exports = {};
|
|
1260
|
+
__export(credential_store_exports, {
|
|
1261
|
+
KEYCHAIN_LOCATION: () => KEYCHAIN_LOCATION,
|
|
1262
|
+
credentialPath: () => credentialPath,
|
|
1263
|
+
readStoredCredential: () => readStoredCredential,
|
|
1264
|
+
writeStoredCredential: () => writeStoredCredential
|
|
1265
|
+
});
|
|
1266
|
+
import { homedir as homedir3 } from "node:os";
|
|
1267
|
+
import { join as join5, dirname as dirname4 } from "node:path";
|
|
1268
|
+
import {
|
|
1269
|
+
existsSync as existsSync3,
|
|
1270
|
+
mkdirSync as mkdirSync2,
|
|
1271
|
+
readFileSync as readFileSync5,
|
|
1272
|
+
writeFileSync as writeFileSync2,
|
|
1273
|
+
chmodSync as chmodSync2,
|
|
1274
|
+
rmSync
|
|
1275
|
+
} from "node:fs";
|
|
1276
|
+
function credentialPath(env = process.env) {
|
|
1277
|
+
const override = env["VO_MCP_CREDENTIALS_PATH"]?.trim();
|
|
1278
|
+
if (override) return override;
|
|
1279
|
+
return join5(homedir3(), ".config", "vo-mcp", "credentials.json");
|
|
1280
|
+
}
|
|
1281
|
+
function keychainEnabled(env, keychain) {
|
|
1282
|
+
const disabled = (env["VO_MCP_DISABLE_KEYCHAIN"] ?? "").trim().toLowerCase();
|
|
1283
|
+
if (disabled === "1" || disabled === "true" || disabled === "yes") return false;
|
|
1284
|
+
return keychain.available();
|
|
1285
|
+
}
|
|
1286
|
+
function deserialize(raw) {
|
|
1287
|
+
try {
|
|
1288
|
+
const parsed = JSON.parse(raw);
|
|
1289
|
+
const refresh = typeof parsed.refresh_token === "string" ? parsed.refresh_token.trim() : "";
|
|
1290
|
+
const apiKey = typeof parsed.api_key === "string" ? parsed.api_key.trim() : "";
|
|
1291
|
+
const voCred = typeof parsed.vo_credential === "string" ? parsed.vo_credential.trim() : "";
|
|
1292
|
+
if (!voCred && (!refresh || !apiKey)) return null;
|
|
1293
|
+
return {
|
|
1294
|
+
...refresh ? { refresh_token: refresh } : {},
|
|
1295
|
+
...apiKey ? { api_key: apiKey } : {},
|
|
1296
|
+
...voCred ? { vo_credential: voCred } : {},
|
|
1297
|
+
...typeof parsed.vo_credential_expires_at === "string" ? { vo_credential_expires_at: parsed.vo_credential_expires_at } : {},
|
|
1298
|
+
...typeof parsed.email === "string" ? { email: parsed.email } : {},
|
|
1299
|
+
...typeof parsed.stored_at === "string" ? { stored_at: parsed.stored_at } : {}
|
|
1300
|
+
};
|
|
1301
|
+
} catch {
|
|
1302
|
+
return null;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
function readFromFile(env) {
|
|
1306
|
+
try {
|
|
1307
|
+
const p = credentialPath(env);
|
|
1308
|
+
if (!existsSync3(p)) return null;
|
|
1309
|
+
return deserialize(readFileSync5(p, "utf8"));
|
|
1310
|
+
} catch {
|
|
1311
|
+
return null;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
function readStoredCredential(env = process.env, keychain = realKeychain) {
|
|
1315
|
+
if (keychainEnabled(env, keychain)) {
|
|
1316
|
+
const raw = keychain.get();
|
|
1317
|
+
const fromKeychain = raw ? deserialize(raw) : null;
|
|
1318
|
+
if (fromKeychain) return fromKeychain;
|
|
1319
|
+
}
|
|
1320
|
+
return readFromFile(env);
|
|
1321
|
+
}
|
|
1322
|
+
function deleteFile(env) {
|
|
1323
|
+
try {
|
|
1324
|
+
rmSync(credentialPath(env), { force: true });
|
|
1325
|
+
} catch {
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
function writeToFile(payload, env) {
|
|
1329
|
+
const p = credentialPath(env);
|
|
1330
|
+
mkdirSync2(dirname4(p), { recursive: true });
|
|
1331
|
+
writeFileSync2(p, `${JSON.stringify(payload, null, 2)}
|
|
1332
|
+
`, { mode: 384 });
|
|
1333
|
+
try {
|
|
1334
|
+
chmodSync2(p, 384);
|
|
1335
|
+
} catch {
|
|
1336
|
+
}
|
|
1337
|
+
return p;
|
|
1338
|
+
}
|
|
1339
|
+
function writeStoredCredential(cred, storedAt, env = process.env, keychain = realKeychain) {
|
|
1340
|
+
const payload = {
|
|
1341
|
+
...cred.refresh_token ? { refresh_token: cred.refresh_token } : {},
|
|
1342
|
+
...cred.api_key ? { api_key: cred.api_key } : {},
|
|
1343
|
+
...cred.vo_credential ? { vo_credential: cred.vo_credential } : {},
|
|
1344
|
+
...cred.vo_credential_expires_at ? { vo_credential_expires_at: cred.vo_credential_expires_at } : {},
|
|
1345
|
+
...cred.email ? { email: cred.email } : {},
|
|
1346
|
+
stored_at: cred.stored_at ?? storedAt
|
|
1347
|
+
};
|
|
1348
|
+
if (keychainEnabled(env, keychain) && keychain.set(JSON.stringify(payload))) {
|
|
1349
|
+
deleteFile(env);
|
|
1350
|
+
return KEYCHAIN_LOCATION;
|
|
1351
|
+
}
|
|
1352
|
+
const p = writeToFile(payload, env);
|
|
1353
|
+
if (keychainEnabled(env, keychain)) keychain.delete();
|
|
1354
|
+
return p;
|
|
1355
|
+
}
|
|
1356
|
+
var realKeychain, KEYCHAIN_LOCATION;
|
|
1357
|
+
var init_credential_store = __esm({
|
|
1358
|
+
"src/cloud/credential-store.ts"() {
|
|
1359
|
+
"use strict";
|
|
1360
|
+
init_keychain();
|
|
1361
|
+
realKeychain = {
|
|
1362
|
+
available: keychainAvailable,
|
|
1363
|
+
get: keychainGet,
|
|
1364
|
+
set: keychainSet,
|
|
1365
|
+
delete: keychainDelete
|
|
1366
|
+
};
|
|
1367
|
+
KEYCHAIN_LOCATION = 'OS keychain (service "vo-mcp")';
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1371
|
+
// src/tools/memory/sync-config.ts
|
|
1372
|
+
var sync_config_exports = {};
|
|
1373
|
+
__export(sync_config_exports, {
|
|
1374
|
+
TOOL_NAME: () => TOOL_NAME22,
|
|
1375
|
+
deriveProjectSlug: () => deriveProjectSlug,
|
|
1376
|
+
description: () => description22,
|
|
1377
|
+
getMemoryDir: () => getMemoryDir,
|
|
1378
|
+
handleSyncConfig: () => handleSyncConfig,
|
|
1379
|
+
inputSchema: () => inputSchema22,
|
|
1380
|
+
isNoopSyncReason: () => isNoopSyncReason,
|
|
1381
|
+
runMemorySync: () => runMemorySync
|
|
1382
|
+
});
|
|
1383
|
+
import { homedir as homedir5 } from "node:os";
|
|
1384
|
+
import { join as join7 } from "node:path";
|
|
1385
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3, readdirSync as readdirSync4 } from "node:fs";
|
|
1386
|
+
function isToolInput22(v) {
|
|
1387
|
+
if (typeof v !== "object" || v === null) return false;
|
|
1388
|
+
const o = v;
|
|
1389
|
+
if (o["action"] !== "pull" && o["action"] !== "push") return false;
|
|
1390
|
+
if (o["cwd"] !== void 0 && typeof o["cwd"] !== "string") return false;
|
|
1391
|
+
return true;
|
|
1392
|
+
}
|
|
1393
|
+
function deriveProjectSlug(cwd) {
|
|
1394
|
+
return cwd.replace(/\\/g, "/").replace(/\/+$/g, "").replace(/^([a-zA-Z]):/, (_m, drive) => `${drive.toUpperCase()}:`).replace(/[^a-zA-Z0-9]/g, "-");
|
|
1395
|
+
}
|
|
1396
|
+
function getMemoryDir(cwd) {
|
|
1397
|
+
const slug = deriveProjectSlug(cwd);
|
|
1398
|
+
return join7(homedir5(), ".claude", "projects", slug, "memory");
|
|
1399
|
+
}
|
|
1400
|
+
async function pullMemory(controlPlaneUrl, token, memoryDir, fetchFn) {
|
|
1401
|
+
const url = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
|
|
1402
|
+
const response = await fetchFn(url, {
|
|
1403
|
+
method: "GET",
|
|
1404
|
+
headers: {
|
|
1405
|
+
authorization: `Bearer ${token}`
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
if (response.status !== 200) {
|
|
1409
|
+
const text = await response.text();
|
|
1410
|
+
throw new Error(`GET /api/v1/agent-config/memory/me returned HTTP ${response.status}: ${text.slice(0, 200)}`);
|
|
1411
|
+
}
|
|
1412
|
+
const data = JSON.parse(await response.text());
|
|
1413
|
+
if (!data.ok || !Array.isArray(data.entries)) {
|
|
1414
|
+
throw new Error("GET /api/v1/agent-config/memory/me response missing ok=true or entries array");
|
|
1415
|
+
}
|
|
1416
|
+
mkdirSync4(memoryDir, { recursive: true });
|
|
1417
|
+
const files = [];
|
|
1418
|
+
for (const entry of data.entries) {
|
|
1419
|
+
const filePath = join7(memoryDir, entry.file_name);
|
|
1420
|
+
writeFileSync3(filePath, entry.content, "utf8");
|
|
1421
|
+
files.push(entry.file_name);
|
|
1422
|
+
}
|
|
1423
|
+
return { pulled: data.entries.length, files };
|
|
1424
|
+
}
|
|
1425
|
+
async function pushMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
|
|
1426
|
+
if (!existsSync5(memoryDir)) {
|
|
1427
|
+
return { pushed: 0, created: 0, updated: 0 };
|
|
1428
|
+
}
|
|
1429
|
+
const localFiles = readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).map((f) => ({
|
|
1430
|
+
file_name: f,
|
|
1431
|
+
content: readFileSync7(join7(memoryDir, f), "utf8"),
|
|
1432
|
+
entry_type: f === "MEMORY.md" ? "index" : "topic"
|
|
1433
|
+
}));
|
|
1434
|
+
if (localFiles.length === 0) {
|
|
1435
|
+
return { pushed: 0, created: 0, updated: 0 };
|
|
1436
|
+
}
|
|
1437
|
+
const getUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
|
|
1438
|
+
const getResponse = await fetchFn(getUrl, {
|
|
1439
|
+
method: "GET",
|
|
1440
|
+
headers: {
|
|
1441
|
+
authorization: `Bearer ${token}`
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
const existingMap = /* @__PURE__ */ new Map();
|
|
1445
|
+
if (getResponse.status === 200) {
|
|
1446
|
+
const getData = JSON.parse(await getResponse.text());
|
|
1447
|
+
if (getData.ok && Array.isArray(getData.entries)) {
|
|
1448
|
+
for (const entry of getData.entries) {
|
|
1449
|
+
existingMap.set(entry.file_name, entry.memory_id);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
let created = 0;
|
|
1454
|
+
let updated = 0;
|
|
1455
|
+
for (const localFile of localFiles) {
|
|
1456
|
+
const memoryId = existingMap.get(localFile.file_name);
|
|
1457
|
+
if (memoryId) {
|
|
1458
|
+
const updateUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/${memoryId}`;
|
|
1459
|
+
const updateBody = {
|
|
1460
|
+
content: localFile.content,
|
|
1461
|
+
session_id: sessionId
|
|
1462
|
+
};
|
|
1463
|
+
const updateResponse = await fetchFn(updateUrl, {
|
|
1464
|
+
method: "PUT",
|
|
1465
|
+
headers: {
|
|
1466
|
+
authorization: `Bearer ${token}`,
|
|
1467
|
+
"content-type": "application/json"
|
|
1468
|
+
},
|
|
1469
|
+
body: JSON.stringify(updateBody)
|
|
1470
|
+
});
|
|
1471
|
+
if (updateResponse.status !== 200) {
|
|
1472
|
+
const text = await updateResponse.text();
|
|
1473
|
+
throw new Error(
|
|
1474
|
+
`PUT /api/v1/agent-config/memory/${memoryId} returned HTTP ${updateResponse.status}: ${text.slice(0, 200)}`
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
const updateData = JSON.parse(await updateResponse.text());
|
|
1478
|
+
if (!updateData.ok) {
|
|
1479
|
+
throw new Error(`PUT /api/v1/agent-config/memory/${memoryId} returned ok=false`);
|
|
1480
|
+
}
|
|
1481
|
+
updated++;
|
|
1482
|
+
} else {
|
|
1483
|
+
const createUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
|
|
1484
|
+
const createBody = {
|
|
1485
|
+
entry_type: localFile.entry_type,
|
|
1486
|
+
file_name: localFile.file_name,
|
|
1487
|
+
content: localFile.content,
|
|
1488
|
+
session_id: sessionId
|
|
1489
|
+
};
|
|
1490
|
+
const createResponse = await fetchFn(createUrl, {
|
|
1491
|
+
method: "POST",
|
|
1492
|
+
headers: {
|
|
1493
|
+
authorization: `Bearer ${token}`,
|
|
1494
|
+
"content-type": "application/json"
|
|
1495
|
+
},
|
|
1496
|
+
body: JSON.stringify(createBody)
|
|
1497
|
+
});
|
|
1498
|
+
if (createResponse.status !== 200 && createResponse.status !== 201) {
|
|
1499
|
+
const text = await createResponse.text();
|
|
1500
|
+
throw new Error(
|
|
1501
|
+
`POST /api/v1/agent-config/memory/me returned HTTP ${createResponse.status}: ${text.slice(0, 200)}`
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
const createData = JSON.parse(await createResponse.text());
|
|
1505
|
+
if (!createData.ok) {
|
|
1506
|
+
throw new Error("POST /api/v1/agent-config/memory/me returned ok=false");
|
|
1507
|
+
}
|
|
1508
|
+
created++;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
return { pushed: localFiles.length, created, updated };
|
|
1512
|
+
}
|
|
1513
|
+
function isNoopSyncReason(reason) {
|
|
1514
|
+
if (!reason) return false;
|
|
1515
|
+
return /not set|No auth configured|Failed to obtain auth token/.test(reason);
|
|
1516
|
+
}
|
|
1517
|
+
async function runMemorySync(action, cwd, sessionId, fetchFn = globalThis.fetch) {
|
|
1518
|
+
const controlPlaneUrl = process.env["VO_CONTROL_PLANE_URL"];
|
|
1519
|
+
if (!controlPlaneUrl) {
|
|
1520
|
+
return { synced: false, reason: "VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled" };
|
|
1521
|
+
}
|
|
1522
|
+
const { createAuthTokenSourceFromEnv: createAuthTokenSourceFromEnv2 } = await Promise.resolve().then(() => (init_auth_token_source(), auth_token_source_exports));
|
|
1523
|
+
const { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
|
|
1524
|
+
const tokenSource = createAuthTokenSourceFromEnv2(process.env, fetchFn, () => readStoredCredential2(process.env));
|
|
1525
|
+
if (!tokenSource) {
|
|
1526
|
+
return { synced: false, reason: "No auth configured. Run `vo-mcp login` to authenticate as an operator." };
|
|
1527
|
+
}
|
|
1528
|
+
const token = await tokenSource.getToken();
|
|
1529
|
+
if (!token) {
|
|
1530
|
+
return { synced: false, reason: "Failed to obtain auth token. Run `vo-mcp login` to re-authenticate." };
|
|
1531
|
+
}
|
|
1532
|
+
const memoryDir = getMemoryDir(cwd);
|
|
1533
|
+
const baseUrl = controlPlaneUrl.replace(/\/+$/, "");
|
|
1534
|
+
try {
|
|
1535
|
+
if (action === "pull") {
|
|
1536
|
+
const result2 = await pullMemory(baseUrl, token, memoryDir, fetchFn);
|
|
1537
|
+
return { synced: true, action: "pull", pulled: result2.pulled, files: result2.files, memory_dir: memoryDir };
|
|
1538
|
+
}
|
|
1539
|
+
const result = await pushMemory(baseUrl, token, memoryDir, sessionId, fetchFn);
|
|
1540
|
+
return {
|
|
1541
|
+
synced: true,
|
|
1542
|
+
action: "push",
|
|
1543
|
+
pushed: result.pushed,
|
|
1544
|
+
created: result.created,
|
|
1545
|
+
updated: result.updated,
|
|
1546
|
+
memory_dir: memoryDir
|
|
1547
|
+
};
|
|
1548
|
+
} catch (err) {
|
|
1549
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1550
|
+
return { synced: false, reason: `Sync failed: ${message}` };
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
async function handleSyncConfig(deps, rawInput, _signal, fetchFn = globalThis.fetch) {
|
|
1554
|
+
if (!isToolInput22(rawInput)) {
|
|
1555
|
+
throw invalidParams(
|
|
1556
|
+
TOOL_NAME22,
|
|
1557
|
+
'invalid input. Required: { action: "pull" | "push" }. Optional: { cwd: "<path>" }.'
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
const cwd = rawInput.cwd?.trim() || process.cwd();
|
|
1561
|
+
const result = await runMemorySync(rawInput.action, cwd, deps.session.sessionId, fetchFn);
|
|
1562
|
+
return jsonContent({ tool: TOOL_NAME22, schema_version: 1, payload: result });
|
|
1563
|
+
}
|
|
1564
|
+
var TOOL_NAME22, inputSchema22, description22;
|
|
1565
|
+
var init_sync_config = __esm({
|
|
1566
|
+
"src/tools/memory/sync-config.ts"() {
|
|
1567
|
+
"use strict";
|
|
1568
|
+
init_common();
|
|
1569
|
+
TOOL_NAME22 = "vo_sync_config";
|
|
1570
|
+
inputSchema22 = {
|
|
1571
|
+
type: "object",
|
|
1572
|
+
properties: {
|
|
1573
|
+
action: {
|
|
1574
|
+
type: "string",
|
|
1575
|
+
enum: ["pull", "push"],
|
|
1576
|
+
description: "pull: download cloud memory to local files. push: upload local files to cloud."
|
|
1577
|
+
},
|
|
1578
|
+
cwd: {
|
|
1579
|
+
type: "string",
|
|
1580
|
+
description: "Working directory to derive project slug from (default: process.cwd())."
|
|
1581
|
+
}
|
|
1582
|
+
},
|
|
1583
|
+
required: ["action"],
|
|
1584
|
+
additionalProperties: false
|
|
1585
|
+
};
|
|
1586
|
+
description22 = "Syncs memory entries between local ~/.claude/projects/<slug>/memory/ and cloud control-plane /api/v1/agent-config/memory/me. Requires operator auth (vo-mcp login). Actions: pull (cloud\u2192local), push (local\u2192cloud). Idempotent; push creates/updates as needed.";
|
|
1587
|
+
}
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1590
|
+
// src/cli.ts
|
|
1591
|
+
import { homedir as homedir6, hostname } from "node:os";
|
|
1592
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
1593
|
+
import { join as join8 } from "node:path";
|
|
1594
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1595
|
+
|
|
1596
|
+
// src/server.ts
|
|
1597
|
+
init_common();
|
|
1598
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1599
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1600
|
+
import {
|
|
1601
|
+
CallToolRequestSchema,
|
|
1602
|
+
ListToolsRequestSchema
|
|
1603
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
1212
1604
|
|
|
1213
1605
|
// src/modes/local.ts
|
|
1214
1606
|
function createLocalMode() {
|
|
@@ -1223,6 +1615,7 @@ function createLocalMode() {
|
|
|
1223
1615
|
}
|
|
1224
1616
|
|
|
1225
1617
|
// src/tools/check-assertion-strength.ts
|
|
1618
|
+
init_common();
|
|
1226
1619
|
var TOOL_NAME = "vo_check_assertion_strength";
|
|
1227
1620
|
var GATE_TYPE = "ratchet";
|
|
1228
1621
|
var MAX_SOURCE_BYTES = 512 * 1024;
|
|
@@ -1319,7 +1712,11 @@ async function handleCheckAssertionStrength(deps, rawInput, _signal) {
|
|
|
1319
1712
|
return jsonContent(envelope);
|
|
1320
1713
|
}
|
|
1321
1714
|
|
|
1715
|
+
// src/tools/check-hollow-test.ts
|
|
1716
|
+
init_common();
|
|
1717
|
+
|
|
1322
1718
|
// src/tools/architecture-review-kb-prefilter.ts
|
|
1719
|
+
init_src();
|
|
1323
1720
|
var DEFAULT_STACK = [
|
|
1324
1721
|
"node",
|
|
1325
1722
|
"node-pnpm-monorepo",
|
|
@@ -1405,6 +1802,7 @@ function formatRulesForPrompt(hits, truncated, domainLabel = "ARCHITECTURAL") {
|
|
|
1405
1802
|
}
|
|
1406
1803
|
|
|
1407
1804
|
// src/tools/kb-metadata-prefilter.ts
|
|
1805
|
+
init_src();
|
|
1408
1806
|
function findRulesByMetadata(opts) {
|
|
1409
1807
|
if (opts.category === void 0 && opts.tagsAny === void 0) {
|
|
1410
1808
|
return {
|
|
@@ -1598,6 +1996,7 @@ async function handleCheckHollowTest(deps, rawInput, signal) {
|
|
|
1598
1996
|
}
|
|
1599
1997
|
|
|
1600
1998
|
// src/tools/verify-answer.ts
|
|
1999
|
+
init_common();
|
|
1601
2000
|
var TOOL_NAME3 = "vo_verify_answer";
|
|
1602
2001
|
var SHALLOW_GATE = "mid-exec-verify";
|
|
1603
2002
|
var DEEP_GATE = "final-deep-verify";
|
|
@@ -1779,6 +2178,9 @@ async function handleVerifyAnswer(deps, rawInput, signal) {
|
|
|
1779
2178
|
return jsonContent(envelope);
|
|
1780
2179
|
}
|
|
1781
2180
|
|
|
2181
|
+
// src/tools/consensus-judgment.ts
|
|
2182
|
+
init_common();
|
|
2183
|
+
|
|
1782
2184
|
// src/consensus/gate-types.ts
|
|
1783
2185
|
var LEGACY_GATE_TYPES = [
|
|
1784
2186
|
"test_assertion",
|
|
@@ -2036,6 +2438,8 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
|
|
|
2036
2438
|
...engineResult.synthesized_verdict.confidence_badge !== void 0 ? { confidence_badge: engineResult.synthesized_verdict.confidence_badge } : {},
|
|
2037
2439
|
// Feature 1 (agreement-gate) — fan-out diagnostics (present iff the gate ran).
|
|
2038
2440
|
...engineResult.fan_out_diagnostics !== void 0 ? { fan_out_diagnostics: engineResult.fan_out_diagnostics } : {},
|
|
2441
|
+
// Stage A7-shadow — adaptive-vs-incumbent comparison (PII-free; present iff shadow on).
|
|
2442
|
+
...engineResult.shadow_synthesis !== void 0 ? { shadow_synthesis: engineResult.shadow_synthesis } : {},
|
|
2039
2443
|
// Source-grounded Tier-4 outputs (present iff the call was source-grounded).
|
|
2040
2444
|
...engineResult.source_grounded === true ? { source_grounded: true } : {},
|
|
2041
2445
|
...engineResult.citation_grade !== void 0 ? { citation_grade: engineResult.citation_grade } : {},
|
|
@@ -2056,6 +2460,8 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
|
|
|
2056
2460
|
}
|
|
2057
2461
|
|
|
2058
2462
|
// src/tools/architecture-review.ts
|
|
2463
|
+
init_common();
|
|
2464
|
+
init_events_writer();
|
|
2059
2465
|
var TOOL_NAME5 = "vo_architecture_review";
|
|
2060
2466
|
var GATE_TYPE3 = "architecture-review";
|
|
2061
2467
|
var MAX_DIFF_BYTES = 1024 * 1024;
|
|
@@ -3300,6 +3706,7 @@ function partitionByAllowlist(findings, allowlist) {
|
|
|
3300
3706
|
}
|
|
3301
3707
|
|
|
3302
3708
|
// src/tools/check-ratchets.ts
|
|
3709
|
+
init_common();
|
|
3303
3710
|
var TOOL_NAME6 = "vo_check_ratchets";
|
|
3304
3711
|
var GATE_TYPE4 = "ratchet";
|
|
3305
3712
|
var ALL_RATCHET_IDS = [
|
|
@@ -3432,6 +3839,7 @@ function buildSummary(report) {
|
|
|
3432
3839
|
}
|
|
3433
3840
|
|
|
3434
3841
|
// src/tools/decompose-dispatch.ts
|
|
3842
|
+
init_common();
|
|
3435
3843
|
var TOOL_NAME7 = "vo_decompose_dispatch";
|
|
3436
3844
|
var GATE_TYPE5 = "plan-review";
|
|
3437
3845
|
var MAX_GOAL_BYTES = 32 * 1024;
|
|
@@ -3709,6 +4117,9 @@ Produce the JSON dispatch plan now.`;
|
|
|
3709
4117
|
return jsonContent(envelope);
|
|
3710
4118
|
}
|
|
3711
4119
|
|
|
4120
|
+
// src/tools/heal/trigger-heal.ts
|
|
4121
|
+
init_common();
|
|
4122
|
+
|
|
3712
4123
|
// src/cloud/admin-callable-client.ts
|
|
3713
4124
|
init_auth_token_source();
|
|
3714
4125
|
init_credential_store();
|
|
@@ -3838,6 +4249,7 @@ function buildAdminCallableClientFromEnv(env = process.env) {
|
|
|
3838
4249
|
}
|
|
3839
4250
|
|
|
3840
4251
|
// src/tools/cloud-call.ts
|
|
4252
|
+
init_common();
|
|
3841
4253
|
var ADMIN_READONLY_GATE_REASON = "admin callables are in read-only mode (VO_ADMIN_CALLABLES_READONLY) \u2014 this write tool is gated to its stub. Unset VO_ADMIN_CALLABLES_READONLY to enable write tools.";
|
|
3842
4254
|
async function buildCloudOrStubResponse(args) {
|
|
3843
4255
|
const inputJson = JSON.stringify(args.normalizedInput);
|
|
@@ -3911,6 +4323,7 @@ async function buildCloudOrStubResponse(args) {
|
|
|
3911
4323
|
}
|
|
3912
4324
|
|
|
3913
4325
|
// src/tools/heal/common-heal.ts
|
|
4326
|
+
init_common();
|
|
3914
4327
|
var HEAL_STUB_REASON = 'cloud-mode not yet wired; tool surface is live, admin-callable wiring pending vo-cloud-tenant-model dispatch (see packages/vo-mcp/src/modes/cloud.ts + EXTRACTION_AUDIT.md "Stub remaining")';
|
|
3915
4328
|
var HEAL_GATE_TYPE = "admin-action";
|
|
3916
4329
|
|
|
@@ -3970,6 +4383,7 @@ async function handleTriggerHeal(deps, rawInput, _signal) {
|
|
|
3970
4383
|
}
|
|
3971
4384
|
|
|
3972
4385
|
// src/tools/heal/fix-retry.ts
|
|
4386
|
+
init_common();
|
|
3973
4387
|
var TOOL_NAME9 = "vo_fix_retry";
|
|
3974
4388
|
var MAX_BATCH = 50;
|
|
3975
4389
|
var ADMIN_PATH_SINGLE = "/api/v1/admin/heal/retry-attempt";
|
|
@@ -4062,6 +4476,7 @@ async function handleFixRetry(deps, rawInput, _signal) {
|
|
|
4062
4476
|
}
|
|
4063
4477
|
|
|
4064
4478
|
// src/tools/heal/fix-clear.ts
|
|
4479
|
+
init_common();
|
|
4065
4480
|
var TOOL_NAME10 = "vo_fix_clear";
|
|
4066
4481
|
var CALLABLE_NAME2 = "voClearFixAttempt";
|
|
4067
4482
|
var ADMIN_PATH2 = "/api/v1/admin/heal/clear-attempt";
|
|
@@ -4105,6 +4520,7 @@ async function handleFixClear(deps, rawInput, _signal) {
|
|
|
4105
4520
|
}
|
|
4106
4521
|
|
|
4107
4522
|
// src/tools/heal/stop-workflow.ts
|
|
4523
|
+
init_common();
|
|
4108
4524
|
var TOOL_NAME11 = "vo_stop_workflow";
|
|
4109
4525
|
var CALLABLE_NAME3 = "voStopWorkflow";
|
|
4110
4526
|
var ADMIN_PATH3 = "/api/v1/admin/workflow/stop";
|
|
@@ -4160,6 +4576,7 @@ async function handleStopWorkflow(deps, rawInput, _signal) {
|
|
|
4160
4576
|
}
|
|
4161
4577
|
|
|
4162
4578
|
// src/tools/heal/get-workflow-runs.ts
|
|
4579
|
+
init_common();
|
|
4163
4580
|
var TOOL_NAME12 = "vo_get_workflow_runs";
|
|
4164
4581
|
var CALLABLE_NAME4 = "voGetWorkflowRuns";
|
|
4165
4582
|
var ADMIN_PATH4 = "/api/v1/admin/workflow/runs";
|
|
@@ -4190,7 +4607,11 @@ async function handleGetWorkflowRuns(deps, rawInput, _signal) {
|
|
|
4190
4607
|
});
|
|
4191
4608
|
}
|
|
4192
4609
|
|
|
4610
|
+
// src/tools/pr/list-pending-prs.ts
|
|
4611
|
+
init_common();
|
|
4612
|
+
|
|
4193
4613
|
// src/tools/pr/common-pr.ts
|
|
4614
|
+
init_common();
|
|
4194
4615
|
var PR_STUB_REASON = 'cloud-mode not yet wired; tool surface is live, admin-callable wiring pending vo-cloud-tenant-model dispatch (see packages/vo-mcp/src/modes/cloud.ts + EXTRACTION_AUDIT.md "Stub remaining")';
|
|
4195
4616
|
var PR_GATE_TYPE = "admin-action";
|
|
4196
4617
|
|
|
@@ -4225,6 +4646,7 @@ async function handleListPendingPRs(deps, rawInput, _signal) {
|
|
|
4225
4646
|
}
|
|
4226
4647
|
|
|
4227
4648
|
// src/tools/pr/merge-pr.ts
|
|
4649
|
+
init_common();
|
|
4228
4650
|
var TOOL_NAME14 = "vo_merge_pr";
|
|
4229
4651
|
var CALLABLE_NAME6 = "voMergePR";
|
|
4230
4652
|
var ADMIN_PATH6 = "/api/v1/admin/pr/merge";
|
|
@@ -4269,6 +4691,7 @@ async function handleMergePR(deps, rawInput, _signal) {
|
|
|
4269
4691
|
}
|
|
4270
4692
|
|
|
4271
4693
|
// src/tools/pr/reject-pr.ts
|
|
4694
|
+
init_common();
|
|
4272
4695
|
var TOOL_NAME15 = "vo_reject_pr";
|
|
4273
4696
|
var CALLABLE_NAME7 = "voRejectPR";
|
|
4274
4697
|
var ADMIN_PATH7 = "/api/v1/admin/pr/reject";
|
|
@@ -4313,6 +4736,7 @@ async function handleRejectPR(deps, rawInput, _signal) {
|
|
|
4313
4736
|
}
|
|
4314
4737
|
|
|
4315
4738
|
// src/tools/pr/approve-all-fixes.ts
|
|
4739
|
+
init_common();
|
|
4316
4740
|
var TOOL_NAME16 = "vo_approve_all_fixes";
|
|
4317
4741
|
var CALLABLE_NAME8 = "voApproveAllFixes";
|
|
4318
4742
|
var ADMIN_PATH8 = "/api/v1/admin/pr/approve-all";
|
|
@@ -4341,6 +4765,7 @@ async function handleApproveAllFixes(deps, rawInput, _signal) {
|
|
|
4341
4765
|
}
|
|
4342
4766
|
|
|
4343
4767
|
// src/tools/pr/reject-and-retry.ts
|
|
4768
|
+
init_common();
|
|
4344
4769
|
var TOOL_NAME17 = "vo_reject_and_retry";
|
|
4345
4770
|
var CALLABLE_NAME9 = "voRejectAndRetry";
|
|
4346
4771
|
var ADMIN_PATH9 = "/api/v1/admin/pr/reject-retry";
|
|
@@ -4385,6 +4810,7 @@ async function handleRejectAndRetry(deps, rawInput, _signal) {
|
|
|
4385
4810
|
}
|
|
4386
4811
|
|
|
4387
4812
|
// src/tools/pr/review-merge.ts
|
|
4813
|
+
init_common();
|
|
4388
4814
|
var TOOL_NAME18 = "vo_review_merge";
|
|
4389
4815
|
var LIST_PATH = "/api/v1/admin/pr/list";
|
|
4390
4816
|
var ENGINE_GATE = "final-deep-verify";
|
|
@@ -4563,6 +4989,9 @@ async function handleReviewMerge(deps, rawInput, signal) {
|
|
|
4563
4989
|
});
|
|
4564
4990
|
}
|
|
4565
4991
|
|
|
4992
|
+
// src/tools/session/report-session-state.ts
|
|
4993
|
+
init_common();
|
|
4994
|
+
|
|
4566
4995
|
// src/tools/session/directive.ts
|
|
4567
4996
|
var SESSION_DIRECTIVE_THRESHOLDS = {
|
|
4568
4997
|
prepare_handoff_pct: 70,
|
|
@@ -4644,7 +5073,7 @@ var inputSchema19 = {
|
|
|
4644
5073
|
required: ["operator_id", "session_id", "agent_type", "context_used_pct"],
|
|
4645
5074
|
additionalProperties: false
|
|
4646
5075
|
};
|
|
4647
|
-
var description19 = "Reports per-session context-window utilization to VO and returns a directive: 'continue' (under 70%), 'prepare_handoff' (70-84%), or 'execute_handoff_now' (\u226585%). Implements V1 launch gate #9 (fleet context lifecycle management) per the official VO roadmap.
|
|
5076
|
+
var description19 = "Reports per-session context-window utilization to VO and returns a directive: 'continue' (under 70%), 'prepare_handoff' (70-84%), or 'execute_handoff_now' (\u226585%). Implements V1 launch gate #9 (fleet context lifecycle management) per the official VO roadmap. Cloud-control-plane mode when VO_CONTROL_PLANE_URL + VO_CONTROL_PLANE_ADMIN_TOKEN + VO_TENANT_ID are set; auto-allocates the session on first report so interactive agents (Claude Code, Cursor, Codex, Continue) appear on the live fleet whiteboard. Stub-local fallback when cloud config is absent or fails. The response shape stays stable across modes (`backend_mode` field in the payload tells the caller which mode produced the verdict).";
|
|
4648
5077
|
function isStringArray2(v, maxItems) {
|
|
4649
5078
|
if (!Array.isArray(v)) return false;
|
|
4650
5079
|
if (v.length > maxItems) return false;
|
|
@@ -4672,30 +5101,62 @@ function isToolInput19(v) {
|
|
|
4672
5101
|
function getCloudConfig() {
|
|
4673
5102
|
const url = process.env["VO_CONTROL_PLANE_URL"];
|
|
4674
5103
|
const token = process.env["VO_CONTROL_PLANE_ADMIN_TOKEN"];
|
|
4675
|
-
|
|
4676
|
-
|
|
5104
|
+
const tenant_id = process.env["VO_TENANT_ID"];
|
|
5105
|
+
if (!url || !token || !tenant_id) return null;
|
|
5106
|
+
return { url, token, tenant_id };
|
|
4677
5107
|
}
|
|
4678
5108
|
async function tryCloudReportState(cloud, input) {
|
|
4679
5109
|
try {
|
|
4680
|
-
const
|
|
5110
|
+
const reportBody = {
|
|
4681
5111
|
context_used_pct: input.context_used_pct
|
|
4682
5112
|
};
|
|
4683
|
-
if (input.current_goal !== void 0)
|
|
5113
|
+
if (input.current_goal !== void 0) reportBody["current_goal"] = input.current_goal;
|
|
4684
5114
|
if (input.recent_files_touched !== void 0) {
|
|
4685
|
-
|
|
5115
|
+
reportBody["recent_files_touched"] = input.recent_files_touched;
|
|
4686
5116
|
}
|
|
4687
5117
|
if (input.recent_tool_uses !== void 0) {
|
|
4688
|
-
|
|
5118
|
+
reportBody["recent_tool_uses"] = input.recent_tool_uses;
|
|
4689
5119
|
}
|
|
4690
|
-
const
|
|
4691
|
-
|
|
5120
|
+
const reportUrl = `${cloud.url}/api/v1/session/${input.session_id}/report-state`;
|
|
5121
|
+
let response = await fetch(reportUrl, {
|
|
4692
5122
|
method: "POST",
|
|
4693
5123
|
headers: {
|
|
4694
5124
|
"Content-Type": "application/json",
|
|
4695
5125
|
"Authorization": `Bearer ${cloud.token}`
|
|
4696
5126
|
},
|
|
4697
|
-
body: JSON.stringify(
|
|
5127
|
+
body: JSON.stringify(reportBody)
|
|
4698
5128
|
});
|
|
5129
|
+
if (response.status === 404) {
|
|
5130
|
+
const allocateBody = {
|
|
5131
|
+
operator_id: input.operator_id,
|
|
5132
|
+
tenant_id: cloud.tenant_id,
|
|
5133
|
+
agent_type: input.agent_type,
|
|
5134
|
+
current_goal: input.current_goal ?? "Interactive session"
|
|
5135
|
+
};
|
|
5136
|
+
if (input.context_used_pct > 0) {
|
|
5137
|
+
allocateBody["initial_context_used_pct"] = input.context_used_pct;
|
|
5138
|
+
}
|
|
5139
|
+
const allocateUrl = `${cloud.url}/api/v1/session`;
|
|
5140
|
+
const allocateResponse = await fetch(allocateUrl, {
|
|
5141
|
+
method: "POST",
|
|
5142
|
+
headers: {
|
|
5143
|
+
"Content-Type": "application/json",
|
|
5144
|
+
"Authorization": `Bearer ${cloud.token}`
|
|
5145
|
+
},
|
|
5146
|
+
body: JSON.stringify(allocateBody)
|
|
5147
|
+
});
|
|
5148
|
+
if (!allocateResponse.ok) {
|
|
5149
|
+
return null;
|
|
5150
|
+
}
|
|
5151
|
+
response = await fetch(reportUrl, {
|
|
5152
|
+
method: "POST",
|
|
5153
|
+
headers: {
|
|
5154
|
+
"Content-Type": "application/json",
|
|
5155
|
+
"Authorization": `Bearer ${cloud.token}`
|
|
5156
|
+
},
|
|
5157
|
+
body: JSON.stringify(reportBody)
|
|
5158
|
+
});
|
|
5159
|
+
}
|
|
4699
5160
|
if (!response.ok) {
|
|
4700
5161
|
return null;
|
|
4701
5162
|
}
|
|
@@ -4751,6 +5212,7 @@ async function handleReportSessionState(deps, rawInput, _signal) {
|
|
|
4751
5212
|
}
|
|
4752
5213
|
|
|
4753
5214
|
// src/tools/session/spawn-successor.ts
|
|
5215
|
+
init_common();
|
|
4754
5216
|
import { spawn } from "node:child_process";
|
|
4755
5217
|
import { homedir as homedir4 } from "node:os";
|
|
4756
5218
|
import { join as join6 } from "node:path";
|
|
@@ -4885,6 +5347,9 @@ async function handleSpawnSuccessor(_deps, rawInput, _signal, spawnImpl = spawn)
|
|
|
4885
5347
|
});
|
|
4886
5348
|
}
|
|
4887
5349
|
|
|
5350
|
+
// src/tools/concierge/dispatch.ts
|
|
5351
|
+
init_common();
|
|
5352
|
+
|
|
4888
5353
|
// src/tools/concierge/common-concierge.ts
|
|
4889
5354
|
var CONCIERGE_STUB_REASON = "cloud mode not active in this MCP runtime \u2014 set VO_CONTROL_PLANE_URL + VO_CONTROL_PLANE_ADMIN_TOKEN to enable. The server-side /api/v1/admin/concierge/dispatch endpoint IS built + deployed (vo-control-plane #5724/#5734); in cloud mode this tool returns the routed pack README + file index.";
|
|
4890
5355
|
var CONCIERGE_GATE_TYPE = "concierge-dispatch";
|
|
@@ -4966,255 +5431,8 @@ async function handleConciergeDispatch(deps, rawInput, _signal) {
|
|
|
4966
5431
|
});
|
|
4967
5432
|
}
|
|
4968
5433
|
|
|
4969
|
-
// src/tools/memory/sync-config.ts
|
|
4970
|
-
import { homedir as homedir5 } from "node:os";
|
|
4971
|
-
import { join as join7 } from "node:path";
|
|
4972
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3, readdirSync as readdirSync4 } from "node:fs";
|
|
4973
|
-
var TOOL_NAME22 = "vo_sync_config";
|
|
4974
|
-
var inputSchema22 = {
|
|
4975
|
-
type: "object",
|
|
4976
|
-
properties: {
|
|
4977
|
-
action: {
|
|
4978
|
-
type: "string",
|
|
4979
|
-
enum: ["pull", "push"],
|
|
4980
|
-
description: "pull: download cloud memory to local files. push: upload local files to cloud."
|
|
4981
|
-
},
|
|
4982
|
-
cwd: {
|
|
4983
|
-
type: "string",
|
|
4984
|
-
description: "Working directory to derive project slug from (default: process.cwd())."
|
|
4985
|
-
}
|
|
4986
|
-
},
|
|
4987
|
-
required: ["action"],
|
|
4988
|
-
additionalProperties: false
|
|
4989
|
-
};
|
|
4990
|
-
var description22 = "Syncs memory entries between local ~/.claude/projects/<slug>/memory/ and cloud control-plane /api/v1/agent-config/memory/me. Requires operator auth (vo-mcp login). Actions: pull (cloud\u2192local), push (local\u2192cloud). Idempotent; push creates/updates as needed.";
|
|
4991
|
-
function isToolInput22(v) {
|
|
4992
|
-
if (typeof v !== "object" || v === null) return false;
|
|
4993
|
-
const o = v;
|
|
4994
|
-
if (o["action"] !== "pull" && o["action"] !== "push") return false;
|
|
4995
|
-
if (o["cwd"] !== void 0 && typeof o["cwd"] !== "string") return false;
|
|
4996
|
-
return true;
|
|
4997
|
-
}
|
|
4998
|
-
function deriveProjectSlug(cwd) {
|
|
4999
|
-
const normalized = cwd.replace(/\\/g, "/");
|
|
5000
|
-
return normalized.replace(/^([A-Z]):/i, (_, drive) => `${drive.toUpperCase()}-`).replace(/\/$/g, "").split("/").join("--").replace(/\s+/g, "-");
|
|
5001
|
-
}
|
|
5002
|
-
function getMemoryDir(cwd) {
|
|
5003
|
-
const slug = deriveProjectSlug(cwd);
|
|
5004
|
-
return join7(homedir5(), ".claude", "projects", slug, "memory");
|
|
5005
|
-
}
|
|
5006
|
-
async function pullMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
|
|
5007
|
-
const url = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
|
|
5008
|
-
const response = await fetchFn(url, {
|
|
5009
|
-
method: "GET",
|
|
5010
|
-
headers: {
|
|
5011
|
-
authorization: `Bearer ${token}`
|
|
5012
|
-
}
|
|
5013
|
-
});
|
|
5014
|
-
if (response.status !== 200) {
|
|
5015
|
-
const text = await response.text();
|
|
5016
|
-
throw new Error(`GET /api/v1/agent-config/memory/me returned HTTP ${response.status}: ${text.slice(0, 200)}`);
|
|
5017
|
-
}
|
|
5018
|
-
const data = JSON.parse(await response.text());
|
|
5019
|
-
if (!data.ok || !Array.isArray(data.entries)) {
|
|
5020
|
-
throw new Error("GET /api/v1/agent-config/memory/me response missing ok=true or entries array");
|
|
5021
|
-
}
|
|
5022
|
-
mkdirSync4(memoryDir, { recursive: true });
|
|
5023
|
-
const files = [];
|
|
5024
|
-
for (const entry of data.entries) {
|
|
5025
|
-
const filePath = join7(memoryDir, entry.file_name);
|
|
5026
|
-
writeFileSync3(filePath, entry.content, "utf8");
|
|
5027
|
-
files.push(entry.file_name);
|
|
5028
|
-
}
|
|
5029
|
-
return { pulled: data.entries.length, files };
|
|
5030
|
-
}
|
|
5031
|
-
async function pushMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
|
|
5032
|
-
if (!existsSync5(memoryDir)) {
|
|
5033
|
-
return { pushed: 0, created: 0, updated: 0 };
|
|
5034
|
-
}
|
|
5035
|
-
const localFiles = readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).map((f) => ({
|
|
5036
|
-
file_name: f,
|
|
5037
|
-
content: readFileSync7(join7(memoryDir, f), "utf8"),
|
|
5038
|
-
entry_type: f === "MEMORY.md" ? "index" : "topic"
|
|
5039
|
-
}));
|
|
5040
|
-
if (localFiles.length === 0) {
|
|
5041
|
-
return { pushed: 0, created: 0, updated: 0 };
|
|
5042
|
-
}
|
|
5043
|
-
const getUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
|
|
5044
|
-
const getResponse = await fetchFn(getUrl, {
|
|
5045
|
-
method: "GET",
|
|
5046
|
-
headers: {
|
|
5047
|
-
authorization: `Bearer ${token}`
|
|
5048
|
-
}
|
|
5049
|
-
});
|
|
5050
|
-
const existingMap = /* @__PURE__ */ new Map();
|
|
5051
|
-
if (getResponse.status === 200) {
|
|
5052
|
-
const getData = JSON.parse(await getResponse.text());
|
|
5053
|
-
if (getData.ok && Array.isArray(getData.entries)) {
|
|
5054
|
-
for (const entry of getData.entries) {
|
|
5055
|
-
existingMap.set(entry.file_name, entry.memory_id);
|
|
5056
|
-
}
|
|
5057
|
-
}
|
|
5058
|
-
}
|
|
5059
|
-
let created = 0;
|
|
5060
|
-
let updated = 0;
|
|
5061
|
-
for (const localFile of localFiles) {
|
|
5062
|
-
const memoryId = existingMap.get(localFile.file_name);
|
|
5063
|
-
if (memoryId) {
|
|
5064
|
-
const updateUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/${memoryId}`;
|
|
5065
|
-
const updateBody = {
|
|
5066
|
-
content: localFile.content,
|
|
5067
|
-
session_id: sessionId
|
|
5068
|
-
};
|
|
5069
|
-
const updateResponse = await fetchFn(updateUrl, {
|
|
5070
|
-
method: "PUT",
|
|
5071
|
-
headers: {
|
|
5072
|
-
authorization: `Bearer ${token}`,
|
|
5073
|
-
"content-type": "application/json"
|
|
5074
|
-
},
|
|
5075
|
-
body: JSON.stringify(updateBody)
|
|
5076
|
-
});
|
|
5077
|
-
if (updateResponse.status !== 200) {
|
|
5078
|
-
const text = await updateResponse.text();
|
|
5079
|
-
throw new Error(
|
|
5080
|
-
`PUT /api/v1/agent-config/memory/${memoryId} returned HTTP ${updateResponse.status}: ${text.slice(0, 200)}`
|
|
5081
|
-
);
|
|
5082
|
-
}
|
|
5083
|
-
const updateData = JSON.parse(await updateResponse.text());
|
|
5084
|
-
if (!updateData.ok) {
|
|
5085
|
-
throw new Error(`PUT /api/v1/agent-config/memory/${memoryId} returned ok=false`);
|
|
5086
|
-
}
|
|
5087
|
-
updated++;
|
|
5088
|
-
} else {
|
|
5089
|
-
const createUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
|
|
5090
|
-
const createBody = {
|
|
5091
|
-
entry_type: localFile.entry_type,
|
|
5092
|
-
file_name: localFile.file_name,
|
|
5093
|
-
content: localFile.content,
|
|
5094
|
-
session_id: sessionId
|
|
5095
|
-
};
|
|
5096
|
-
const createResponse = await fetchFn(createUrl, {
|
|
5097
|
-
method: "POST",
|
|
5098
|
-
headers: {
|
|
5099
|
-
authorization: `Bearer ${token}`,
|
|
5100
|
-
"content-type": "application/json"
|
|
5101
|
-
},
|
|
5102
|
-
body: JSON.stringify(createBody)
|
|
5103
|
-
});
|
|
5104
|
-
if (createResponse.status !== 200 && createResponse.status !== 201) {
|
|
5105
|
-
const text = await createResponse.text();
|
|
5106
|
-
throw new Error(
|
|
5107
|
-
`POST /api/v1/agent-config/memory/me returned HTTP ${createResponse.status}: ${text.slice(0, 200)}`
|
|
5108
|
-
);
|
|
5109
|
-
}
|
|
5110
|
-
const createData = JSON.parse(await createResponse.text());
|
|
5111
|
-
if (!createData.ok) {
|
|
5112
|
-
throw new Error("POST /api/v1/agent-config/memory/me returned ok=false");
|
|
5113
|
-
}
|
|
5114
|
-
created++;
|
|
5115
|
-
}
|
|
5116
|
-
}
|
|
5117
|
-
return { pushed: localFiles.length, created, updated };
|
|
5118
|
-
}
|
|
5119
|
-
async function handleSyncConfig(deps, rawInput, _signal, fetchFn = globalThis.fetch) {
|
|
5120
|
-
if (!isToolInput22(rawInput)) {
|
|
5121
|
-
throw invalidParams(
|
|
5122
|
-
TOOL_NAME22,
|
|
5123
|
-
'invalid input. Required: { action: "pull" | "push" }. Optional: { cwd: "<path>" }.'
|
|
5124
|
-
);
|
|
5125
|
-
}
|
|
5126
|
-
const controlPlaneUrl = process.env["VO_CONTROL_PLANE_URL"];
|
|
5127
|
-
if (!controlPlaneUrl) {
|
|
5128
|
-
return jsonContent({
|
|
5129
|
-
tool: TOOL_NAME22,
|
|
5130
|
-
schema_version: 1,
|
|
5131
|
-
payload: {
|
|
5132
|
-
synced: false,
|
|
5133
|
-
reason: "VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled"
|
|
5134
|
-
}
|
|
5135
|
-
});
|
|
5136
|
-
}
|
|
5137
|
-
const { createAuthTokenSourceFromEnv: createAuthTokenSourceFromEnv2 } = await Promise.resolve().then(() => (init_auth_token_source(), auth_token_source_exports));
|
|
5138
|
-
const { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
|
|
5139
|
-
const tokenSource = createAuthTokenSourceFromEnv2(process.env, fetchFn, () => readStoredCredential2(process.env));
|
|
5140
|
-
if (!tokenSource) {
|
|
5141
|
-
return jsonContent({
|
|
5142
|
-
tool: TOOL_NAME22,
|
|
5143
|
-
schema_version: 1,
|
|
5144
|
-
payload: {
|
|
5145
|
-
synced: false,
|
|
5146
|
-
reason: "No auth configured. Run `vo-mcp login` to authenticate as an operator."
|
|
5147
|
-
}
|
|
5148
|
-
});
|
|
5149
|
-
}
|
|
5150
|
-
const token = await tokenSource.getToken();
|
|
5151
|
-
if (!token) {
|
|
5152
|
-
return jsonContent({
|
|
5153
|
-
tool: TOOL_NAME22,
|
|
5154
|
-
schema_version: 1,
|
|
5155
|
-
payload: {
|
|
5156
|
-
synced: false,
|
|
5157
|
-
reason: "Failed to obtain auth token. Run `vo-mcp login` to re-authenticate."
|
|
5158
|
-
}
|
|
5159
|
-
});
|
|
5160
|
-
}
|
|
5161
|
-
const cwd = rawInput.cwd?.trim() || process.cwd();
|
|
5162
|
-
const memoryDir = getMemoryDir(cwd);
|
|
5163
|
-
try {
|
|
5164
|
-
if (rawInput.action === "pull") {
|
|
5165
|
-
const result = await pullMemory(
|
|
5166
|
-
controlPlaneUrl.replace(/\/+$/, ""),
|
|
5167
|
-
token,
|
|
5168
|
-
memoryDir,
|
|
5169
|
-
deps.sessionId,
|
|
5170
|
-
fetchFn
|
|
5171
|
-
);
|
|
5172
|
-
return jsonContent({
|
|
5173
|
-
tool: TOOL_NAME22,
|
|
5174
|
-
schema_version: 1,
|
|
5175
|
-
payload: {
|
|
5176
|
-
synced: true,
|
|
5177
|
-
action: "pull",
|
|
5178
|
-
pulled: result.pulled,
|
|
5179
|
-
files: result.files,
|
|
5180
|
-
memory_dir: memoryDir
|
|
5181
|
-
}
|
|
5182
|
-
});
|
|
5183
|
-
} else {
|
|
5184
|
-
const result = await pushMemory(
|
|
5185
|
-
controlPlaneUrl.replace(/\/+$/, ""),
|
|
5186
|
-
token,
|
|
5187
|
-
memoryDir,
|
|
5188
|
-
deps.sessionId,
|
|
5189
|
-
fetchFn
|
|
5190
|
-
);
|
|
5191
|
-
return jsonContent({
|
|
5192
|
-
tool: TOOL_NAME22,
|
|
5193
|
-
schema_version: 1,
|
|
5194
|
-
payload: {
|
|
5195
|
-
synced: true,
|
|
5196
|
-
action: "push",
|
|
5197
|
-
pushed: result.pushed,
|
|
5198
|
-
created: result.created,
|
|
5199
|
-
updated: result.updated,
|
|
5200
|
-
memory_dir: memoryDir
|
|
5201
|
-
}
|
|
5202
|
-
});
|
|
5203
|
-
}
|
|
5204
|
-
} catch (err) {
|
|
5205
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
5206
|
-
return jsonContent({
|
|
5207
|
-
tool: TOOL_NAME22,
|
|
5208
|
-
schema_version: 1,
|
|
5209
|
-
payload: {
|
|
5210
|
-
synced: false,
|
|
5211
|
-
reason: `Sync failed: ${message}`
|
|
5212
|
-
}
|
|
5213
|
-
});
|
|
5214
|
-
}
|
|
5215
|
-
}
|
|
5216
|
-
|
|
5217
5434
|
// src/server.ts
|
|
5435
|
+
init_sync_config();
|
|
5218
5436
|
function buildToolRegistry() {
|
|
5219
5437
|
return {
|
|
5220
5438
|
[TOOL_NAME]: {
|
|
@@ -5569,6 +5787,9 @@ function createSqliteCache(options) {
|
|
|
5569
5787
|
};
|
|
5570
5788
|
}
|
|
5571
5789
|
|
|
5790
|
+
// src/cli.ts
|
|
5791
|
+
init_events_writer();
|
|
5792
|
+
|
|
5572
5793
|
// src/ratchets/stub-client.ts
|
|
5573
5794
|
var HOLLOW_PATTERNS = [
|
|
5574
5795
|
{
|
|
@@ -5656,6 +5877,8 @@ function buildSummary2(args) {
|
|
|
5656
5877
|
}
|
|
5657
5878
|
|
|
5658
5879
|
// src/consensus/engine-client.ts
|
|
5880
|
+
init_events_writer();
|
|
5881
|
+
init_common();
|
|
5659
5882
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
5660
5883
|
|
|
5661
5884
|
// src/consensus/null-client.ts
|
|
@@ -5724,6 +5947,25 @@ function mapFanOutDiagnostics(fd) {
|
|
|
5724
5947
|
refused: fd.refused
|
|
5725
5948
|
};
|
|
5726
5949
|
}
|
|
5950
|
+
var SHADOW_SYNTHESIS_ENV_VAR = "VO_CONSENSUS_SHADOW";
|
|
5951
|
+
function shadowEnabled(env) {
|
|
5952
|
+
const raw = (env ?? {})[SHADOW_SYNTHESIS_ENV_VAR];
|
|
5953
|
+
if (raw === void 0) return true;
|
|
5954
|
+
const norm = raw.trim().toLowerCase();
|
|
5955
|
+
return !(norm === "0" || norm === "false" || norm === "no" || norm === "off" || norm === "");
|
|
5956
|
+
}
|
|
5957
|
+
function mapShadowSynthesis(s) {
|
|
5958
|
+
if (s === void 0) return void 0;
|
|
5959
|
+
return {
|
|
5960
|
+
incumbent: { verdict: s.incumbent.verdict, confidence: s.incumbent.confidence, synthesizer: s.incumbent.synthesizer },
|
|
5961
|
+
adaptive: {
|
|
5962
|
+
verdict: s.adaptive.verdict,
|
|
5963
|
+
confidence: s.adaptive.confidence,
|
|
5964
|
+
...s.adaptive.calibrated_confidence !== void 0 ? { calibrated_confidence: s.adaptive.calibrated_confidence } : {}
|
|
5965
|
+
},
|
|
5966
|
+
agree: s.agree
|
|
5967
|
+
};
|
|
5968
|
+
}
|
|
5727
5969
|
function mapCitationGrade(cg) {
|
|
5728
5970
|
if (cg === void 0) return void 0;
|
|
5729
5971
|
return {
|
|
@@ -5864,7 +6106,12 @@ function createEngineConsensusClient(options) {
|
|
|
5864
6106
|
const engineOptions = {
|
|
5865
6107
|
panel,
|
|
5866
6108
|
...options.per_model_timeout_ms !== void 0 ? { per_model_timeout_ms: options.per_model_timeout_ms } : {},
|
|
5867
|
-
...agreementGate !== void 0 ? { agreement_gate: agreementGate } : {}
|
|
6109
|
+
...agreementGate !== void 0 ? { agreement_gate: agreementGate } : {},
|
|
6110
|
+
// Stage A7-shadow: run the adaptive verdict alongside the live one for grading.
|
|
6111
|
+
// Cheap (pure log-odds over already-fetched verdicts; no extra model calls),
|
|
6112
|
+
// PII-free, and never alters the live verdict. ON by default; kill with
|
|
6113
|
+
// VO_CONSENSUS_SHADOW=0. Cold-start has no skill registry → neutral priors.
|
|
6114
|
+
shadow_synthesis: { enabled: shadowEnabled(options.env) }
|
|
5868
6115
|
};
|
|
5869
6116
|
const sources = request.source_urls;
|
|
5870
6117
|
const useSourceGrounded = sources !== void 0 && sources.length > 0 && typeof engine.runSourceGroundedConsensus === "function";
|
|
@@ -5920,6 +6167,8 @@ function createEngineConsensusClient(options) {
|
|
|
5920
6167
|
...sourceExtras?.escalation_reason !== void 0 ? { escalation_reason: sourceExtras.escalation_reason } : response.escalation_reason !== void 0 ? { escalation_reason: response.escalation_reason } : {},
|
|
5921
6168
|
// Feature 1 (agreement-gate) — fan-out diagnostics (additive telemetry).
|
|
5922
6169
|
...mapFanOutDiagnostics(response.fan_out_diagnostics) !== void 0 ? { fan_out_diagnostics: mapFanOutDiagnostics(response.fan_out_diagnostics) } : {},
|
|
6170
|
+
// Stage A7-shadow — adaptive-vs-incumbent comparison (PII-free; present iff shadow on).
|
|
6171
|
+
...mapShadowSynthesis(response.shadow_synthesis) !== void 0 ? { shadow_synthesis: mapShadowSynthesis(response.shadow_synthesis) } : {},
|
|
5923
6172
|
// Source-grounded additive outputs (Tier-4 features).
|
|
5924
6173
|
...useSourceGrounded ? { source_grounded: true } : {},
|
|
5925
6174
|
...sourceExtras?.citation_grade !== void 0 ? { citation_grade: sourceExtras.citation_grade } : {},
|
|
@@ -6354,6 +6603,7 @@ async function exchangeForVoCredential(opts) {
|
|
|
6354
6603
|
}
|
|
6355
6604
|
|
|
6356
6605
|
// src/cli.ts
|
|
6606
|
+
init_common();
|
|
6357
6607
|
function defaultCacheDbPath() {
|
|
6358
6608
|
const env = process.env["VO_MCP_DB_PATH"];
|
|
6359
6609
|
if (env && env.length > 0) return env;
|
|
@@ -6486,6 +6736,31 @@ if (process.argv[2] === "login") {
|
|
|
6486
6736
|
console.error("[vo-mcp] login failed:", err instanceof Error ? err.message : String(err));
|
|
6487
6737
|
process.exit(1);
|
|
6488
6738
|
});
|
|
6739
|
+
} else if (process.argv[2] === "sync") {
|
|
6740
|
+
const action = process.argv[3];
|
|
6741
|
+
if (action !== "push" && action !== "pull") {
|
|
6742
|
+
console.error("[vo-mcp] usage: vo-mcp sync <push|pull> [--cwd <path>]");
|
|
6743
|
+
process.exit(2);
|
|
6744
|
+
}
|
|
6745
|
+
const cwdFlag = process.argv.indexOf("--cwd");
|
|
6746
|
+
const cwd = cwdFlag >= 0 && typeof process.argv[cwdFlag + 1] === "string" ? process.argv[cwdFlag + 1] : process.cwd();
|
|
6747
|
+
const sessionId = randomUUID5();
|
|
6748
|
+
Promise.resolve().then(() => (init_sync_config(), sync_config_exports)).then(async ({ runMemorySync: runMemorySync2, isNoopSyncReason: isNoopSyncReason2 }) => {
|
|
6749
|
+
const r = await runMemorySync2(action, cwd, sessionId);
|
|
6750
|
+
if (r.synced) {
|
|
6751
|
+
console.error(`[vo-mcp] sync ${action} ok: ${JSON.stringify(r)}`);
|
|
6752
|
+
process.exit(0);
|
|
6753
|
+
}
|
|
6754
|
+
if (isNoopSyncReason2(r.reason)) {
|
|
6755
|
+
console.error(`[vo-mcp] sync ${action} skipped: ${r.reason}`);
|
|
6756
|
+
process.exit(0);
|
|
6757
|
+
}
|
|
6758
|
+
console.error(`[vo-mcp] sync ${action} failed: ${r.reason}`);
|
|
6759
|
+
process.exit(1);
|
|
6760
|
+
}).catch((err) => {
|
|
6761
|
+
console.error("[vo-mcp] sync fatal:", err instanceof Error ? err.message : String(err));
|
|
6762
|
+
process.exit(1);
|
|
6763
|
+
});
|
|
6489
6764
|
} else {
|
|
6490
6765
|
main().catch((err) => {
|
|
6491
6766
|
console.error("[vo-mcp] fatal:", err);
|