@triflux/core 10.87.0 → 10.88.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hub/bridge.mjs +411 -5
- package/hub/lib/jev-addon.mjs +1675 -0
- package/hub/team/pane.mjs +99 -0
- package/hub/team/retry-state-machine.mjs +22 -2
- package/hud/hud-qos-status.mjs +45 -16
- package/hud/renderers.mjs +13 -3
- package/hud/session-status.mjs +46 -0
- package/hud/tmux-status.mjs +140 -0
- package/package.json +1 -1
- package/scripts/lib/doctor-env-checks.mjs +377 -0
- package/scripts/lib/mcp-filter.mjs +86 -3
|
@@ -1,4 +1,381 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import {
|
|
4
|
+
chmodSync,
|
|
5
|
+
copyFileSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
realpathSync,
|
|
9
|
+
renameSync,
|
|
10
|
+
unlinkSync,
|
|
11
|
+
writeFileSync,
|
|
12
|
+
} from "node:fs";
|
|
13
|
+
|
|
14
|
+
const defaultFs = {
|
|
15
|
+
chmodSync,
|
|
16
|
+
copyFileSync,
|
|
17
|
+
existsSync,
|
|
18
|
+
readFileSync,
|
|
19
|
+
realpathSync,
|
|
20
|
+
renameSync,
|
|
21
|
+
unlinkSync,
|
|
22
|
+
writeFileSync,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// ARM64 Mach-O agy 바이너리의 time.After 함수와 유휴 대기 호출부 시그니처 (#1022 peer-wake 패치)
|
|
26
|
+
export const AGY_TIME_AFTER_SIG = Buffer.from(
|
|
27
|
+
"900b40f9ff6330eb29010054fe0f1ef8fd831ff8fd2300d16affff97000040f9fd835ff8fe0742f8c0035fd6",
|
|
28
|
+
"hex",
|
|
29
|
+
);
|
|
30
|
+
export const AGY_PEER_WAKE_ORIG_CALL = Buffer.from(
|
|
31
|
+
"e13740f9220041a9423040f9210c40a9e40302aae20303aa80003fd6",
|
|
32
|
+
"hex",
|
|
33
|
+
);
|
|
34
|
+
export const AGY_PEER_WAKE_PATCH_PREFIX = Buffer.from(
|
|
35
|
+
"1f2003d51f2003d51f2003d51f2003d5004099524073a772",
|
|
36
|
+
"hex",
|
|
37
|
+
);
|
|
38
|
+
export const AGY_PEER_WAKE_ORIG_STP = Buffer.from("e08708a9", "hex");
|
|
39
|
+
export const AGY_PEER_WAKE_PATCH_STP = Buffer.from("e0ff08a9", "hex");
|
|
40
|
+
const AGY_PEER_WAKE_STP_DELTA = 0x38;
|
|
41
|
+
|
|
42
|
+
export function encodeArm64Bl(pcOffset, targetOffset) {
|
|
43
|
+
const delta = targetOffset - pcOffset;
|
|
44
|
+
if (delta % 4 !== 0 || delta < -0x8000000 || delta > 0x7fffffc) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const imm26 = (delta >> 2) & 0x03ffffff;
|
|
48
|
+
const out = Buffer.alloc(4);
|
|
49
|
+
out.writeUInt32LE((0x94000000 | imm26) >>> 0, 0);
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function findUniqueOffset(buf, pattern) {
|
|
54
|
+
const first = buf.indexOf(pattern);
|
|
55
|
+
if (first === -1) return -1;
|
|
56
|
+
if (buf.indexOf(pattern, first + 1) !== -1) return -2;
|
|
57
|
+
return first;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function inspectAgyPeerWakePatch({
|
|
61
|
+
agyPath,
|
|
62
|
+
platform = process.platform,
|
|
63
|
+
arch = process.arch,
|
|
64
|
+
fs = defaultFs,
|
|
65
|
+
} = {}) {
|
|
66
|
+
if (platform !== "darwin" || arch !== "arm64") {
|
|
67
|
+
return { ok: true, status: "skipped", platform, arch };
|
|
68
|
+
}
|
|
69
|
+
if (!agyPath) {
|
|
70
|
+
return { ok: true, status: "skipped", reason: "agy-not-installed" };
|
|
71
|
+
}
|
|
72
|
+
let resolvedPath = agyPath;
|
|
73
|
+
try {
|
|
74
|
+
resolvedPath = fs.realpathSync(agyPath);
|
|
75
|
+
} catch {
|
|
76
|
+
return { ok: true, status: "skipped", reason: "agy-not-found" };
|
|
77
|
+
}
|
|
78
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
79
|
+
return { ok: true, status: "skipped", reason: "agy-not-found" };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const patchJsonPath = `${resolvedPath}.patch.json`;
|
|
83
|
+
let buf;
|
|
84
|
+
try {
|
|
85
|
+
buf = fs.readFileSync(resolvedPath);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
status: "error",
|
|
90
|
+
patchable: false,
|
|
91
|
+
path: resolvedPath,
|
|
92
|
+
patchJsonPath,
|
|
93
|
+
reason: "read-failed",
|
|
94
|
+
message: error?.message || String(error),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const sha256 = createHash("sha256").update(buf).digest("hex");
|
|
98
|
+
const timeAfterOffset = findUniqueOffset(buf, AGY_TIME_AFTER_SIG);
|
|
99
|
+
if (timeAfterOffset < 0) {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
status: "unsupported",
|
|
103
|
+
patchable: false,
|
|
104
|
+
path: resolvedPath,
|
|
105
|
+
patchJsonPath,
|
|
106
|
+
sha256,
|
|
107
|
+
reason:
|
|
108
|
+
timeAfterOffset === -2
|
|
109
|
+
? "ambiguous-time-after-signature"
|
|
110
|
+
: "time-after-signature-not-found",
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const patchedOffset = findUniqueOffset(buf, AGY_PEER_WAKE_PATCH_PREFIX);
|
|
115
|
+
if (
|
|
116
|
+
patchedOffset >= 0 &&
|
|
117
|
+
patchedOffset + AGY_PEER_WAKE_STP_DELTA + 4 <= buf.length
|
|
118
|
+
) {
|
|
119
|
+
const expectedBl = encodeArm64Bl(
|
|
120
|
+
patchedOffset + AGY_PEER_WAKE_PATCH_PREFIX.length,
|
|
121
|
+
timeAfterOffset,
|
|
122
|
+
);
|
|
123
|
+
const actualBl = buf.subarray(
|
|
124
|
+
patchedOffset + AGY_PEER_WAKE_PATCH_PREFIX.length,
|
|
125
|
+
patchedOffset + AGY_PEER_WAKE_PATCH_PREFIX.length + 4,
|
|
126
|
+
);
|
|
127
|
+
const actualStp = buf.subarray(
|
|
128
|
+
patchedOffset + AGY_PEER_WAKE_STP_DELTA,
|
|
129
|
+
patchedOffset + AGY_PEER_WAKE_STP_DELTA + 4,
|
|
130
|
+
);
|
|
131
|
+
if (
|
|
132
|
+
expectedBl &&
|
|
133
|
+
actualBl.equals(expectedBl) &&
|
|
134
|
+
actualStp.equals(AGY_PEER_WAKE_PATCH_STP)
|
|
135
|
+
) {
|
|
136
|
+
let metadataOk = false;
|
|
137
|
+
if (fs.existsSync(patchJsonPath)) {
|
|
138
|
+
try {
|
|
139
|
+
const meta = JSON.parse(fs.readFileSync(patchJsonPath, "utf8"));
|
|
140
|
+
metadataOk =
|
|
141
|
+
meta?.sha256 === sha256 &&
|
|
142
|
+
Array.isArray(meta?.patches) &&
|
|
143
|
+
meta.patches.includes("peer-wake");
|
|
144
|
+
} catch {
|
|
145
|
+
metadataOk = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (metadataOk) {
|
|
149
|
+
return {
|
|
150
|
+
ok: true,
|
|
151
|
+
status: "patched",
|
|
152
|
+
patchable: false,
|
|
153
|
+
binaryPatched: true,
|
|
154
|
+
path: resolvedPath,
|
|
155
|
+
patchJsonPath,
|
|
156
|
+
sha256,
|
|
157
|
+
callOffset: patchedOffset,
|
|
158
|
+
timeAfterOffset,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
ok: false,
|
|
163
|
+
status: "metadata-outdated",
|
|
164
|
+
patchable: true,
|
|
165
|
+
binaryPatched: true,
|
|
166
|
+
path: resolvedPath,
|
|
167
|
+
patchJsonPath,
|
|
168
|
+
sha256,
|
|
169
|
+
callOffset: patchedOffset,
|
|
170
|
+
timeAfterOffset,
|
|
171
|
+
fix: "tfx doctor --fix",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const origOffset = findUniqueOffset(buf, AGY_PEER_WAKE_ORIG_CALL);
|
|
177
|
+
if (
|
|
178
|
+
origOffset >= 0 &&
|
|
179
|
+
origOffset + AGY_PEER_WAKE_STP_DELTA + 4 <= buf.length
|
|
180
|
+
) {
|
|
181
|
+
const expectedBl = encodeArm64Bl(
|
|
182
|
+
origOffset + AGY_PEER_WAKE_PATCH_PREFIX.length,
|
|
183
|
+
timeAfterOffset,
|
|
184
|
+
);
|
|
185
|
+
const actualStp = buf.subarray(
|
|
186
|
+
origOffset + AGY_PEER_WAKE_STP_DELTA,
|
|
187
|
+
origOffset + AGY_PEER_WAKE_STP_DELTA + 4,
|
|
188
|
+
);
|
|
189
|
+
if (expectedBl && actualStp.equals(AGY_PEER_WAKE_ORIG_STP)) {
|
|
190
|
+
return {
|
|
191
|
+
ok: false,
|
|
192
|
+
status: "unpatched",
|
|
193
|
+
patchable: true,
|
|
194
|
+
binaryPatched: false,
|
|
195
|
+
path: resolvedPath,
|
|
196
|
+
patchJsonPath,
|
|
197
|
+
sha256,
|
|
198
|
+
callOffset: origOffset,
|
|
199
|
+
timeAfterOffset,
|
|
200
|
+
fix: "tfx doctor --fix",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
ok: false,
|
|
207
|
+
status: "unsupported",
|
|
208
|
+
patchable: false,
|
|
209
|
+
path: resolvedPath,
|
|
210
|
+
patchJsonPath,
|
|
211
|
+
sha256,
|
|
212
|
+
reason:
|
|
213
|
+
origOffset === -2
|
|
214
|
+
? "ambiguous-call-site-signature"
|
|
215
|
+
: "call-site-signature-not-found",
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function applyAgyPeerWakePatch({
|
|
220
|
+
agyPath,
|
|
221
|
+
version,
|
|
222
|
+
platform = process.platform,
|
|
223
|
+
arch = process.arch,
|
|
224
|
+
fs = defaultFs,
|
|
225
|
+
execFileSyncFn = execFileSync,
|
|
226
|
+
} = {}) {
|
|
227
|
+
const state = inspectAgyPeerWakePatch({ agyPath, platform, arch, fs });
|
|
228
|
+
if (state.status === "patched" || state.status === "skipped") {
|
|
229
|
+
return { ...state, changed: false };
|
|
230
|
+
}
|
|
231
|
+
if (!state.patchable) {
|
|
232
|
+
return { ...state, ok: false, changed: false };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let resolvedVersion = version || "";
|
|
236
|
+
if (!resolvedVersion) {
|
|
237
|
+
try {
|
|
238
|
+
const out = String(
|
|
239
|
+
execFileSyncFn(state.path, ["--version"], {
|
|
240
|
+
encoding: "utf8",
|
|
241
|
+
timeout: 5000,
|
|
242
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
243
|
+
}) || "",
|
|
244
|
+
);
|
|
245
|
+
resolvedVersion = out.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] || "unknown";
|
|
246
|
+
} catch {
|
|
247
|
+
resolvedVersion = "unknown";
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let finalSha256 = state.sha256;
|
|
252
|
+
let backupEntry = null;
|
|
253
|
+
if (state.status === "unpatched") {
|
|
254
|
+
const rawBuf = fs.readFileSync(state.path);
|
|
255
|
+
const currentSha256 = createHash("sha256").update(rawBuf).digest("hex");
|
|
256
|
+
if (currentSha256 !== state.sha256) {
|
|
257
|
+
throw new Error("agy binary changed during patch preparation");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let backupKey = `${resolvedVersion}-orig`;
|
|
261
|
+
let backupPath = `${state.path}.bak-${backupKey}`;
|
|
262
|
+
if (fs.existsSync(backupPath)) {
|
|
263
|
+
const existingBackupSha = createHash("sha256")
|
|
264
|
+
.update(fs.readFileSync(backupPath))
|
|
265
|
+
.digest("hex");
|
|
266
|
+
if (existingBackupSha !== state.sha256) {
|
|
267
|
+
backupKey = `${resolvedVersion}-orig-${state.sha256.slice(0, 8)}`;
|
|
268
|
+
backupPath = `${state.path}.bak-${backupKey}`;
|
|
269
|
+
if (fs.existsSync(backupPath)) {
|
|
270
|
+
const suffixBackupSha = createHash("sha256")
|
|
271
|
+
.update(fs.readFileSync(backupPath))
|
|
272
|
+
.digest("hex");
|
|
273
|
+
if (suffixBackupSha !== state.sha256) {
|
|
274
|
+
throw new Error(`agy backup conflict: ${backupPath}`);
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
fs.copyFileSync(state.path, backupPath);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
fs.copyFileSync(state.path, backupPath);
|
|
282
|
+
}
|
|
283
|
+
backupEntry = { key: backupKey, path: backupPath, sha256: state.sha256 };
|
|
284
|
+
|
|
285
|
+
const buf = Buffer.from(rawBuf);
|
|
286
|
+
const expectedBl = encodeArm64Bl(
|
|
287
|
+
state.callOffset + AGY_PEER_WAKE_PATCH_PREFIX.length,
|
|
288
|
+
state.timeAfterOffset,
|
|
289
|
+
);
|
|
290
|
+
AGY_PEER_WAKE_PATCH_PREFIX.copy(buf, state.callOffset);
|
|
291
|
+
expectedBl.copy(buf, state.callOffset + AGY_PEER_WAKE_PATCH_PREFIX.length);
|
|
292
|
+
AGY_PEER_WAKE_PATCH_STP.copy(
|
|
293
|
+
buf,
|
|
294
|
+
state.callOffset + AGY_PEER_WAKE_STP_DELTA,
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const tmpPath = `${state.path}.tmp-${process.pid}`;
|
|
298
|
+
fs.writeFileSync(tmpPath, buf, { mode: 0o755 });
|
|
299
|
+
try {
|
|
300
|
+
fs.chmodSync(tmpPath, 0o755);
|
|
301
|
+
execFileSyncFn("codesign", ["-f", "-s", "-", tmpPath], {
|
|
302
|
+
stdio: "ignore",
|
|
303
|
+
timeout: 30000,
|
|
304
|
+
});
|
|
305
|
+
const preRenameSha256 = createHash("sha256")
|
|
306
|
+
.update(fs.readFileSync(state.path))
|
|
307
|
+
.digest("hex");
|
|
308
|
+
if (preRenameSha256 !== state.sha256) {
|
|
309
|
+
throw new Error("agy binary changed before atomic replace");
|
|
310
|
+
}
|
|
311
|
+
fs.renameSync(tmpPath, state.path);
|
|
312
|
+
} catch (error) {
|
|
313
|
+
try {
|
|
314
|
+
fs.unlinkSync(tmpPath);
|
|
315
|
+
} catch {}
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
finalSha256 = createHash("sha256")
|
|
319
|
+
.update(fs.readFileSync(state.path))
|
|
320
|
+
.digest("hex");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
let existingMeta = {};
|
|
324
|
+
if (fs.existsSync(state.patchJsonPath)) {
|
|
325
|
+
try {
|
|
326
|
+
const parsed = JSON.parse(fs.readFileSync(state.patchJsonPath, "utf8"));
|
|
327
|
+
existingMeta =
|
|
328
|
+
parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
329
|
+
? parsed
|
|
330
|
+
: {};
|
|
331
|
+
} catch {
|
|
332
|
+
existingMeta = {};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const patches = Array.isArray(existingMeta.patches)
|
|
336
|
+
? [...new Set([...existingMeta.patches, "peer-wake"])]
|
|
337
|
+
: ["peer-wake"];
|
|
338
|
+
const backups = {
|
|
339
|
+
...(backupEntry
|
|
340
|
+
? {
|
|
341
|
+
[backupEntry.key]: {
|
|
342
|
+
path: backupEntry.path,
|
|
343
|
+
sha256: backupEntry.sha256,
|
|
344
|
+
},
|
|
345
|
+
}
|
|
346
|
+
: {}),
|
|
347
|
+
...(existingMeta.backups && typeof existingMeta.backups === "object"
|
|
348
|
+
? existingMeta.backups
|
|
349
|
+
: {}),
|
|
350
|
+
};
|
|
351
|
+
const nextMeta = {
|
|
352
|
+
...existingMeta,
|
|
353
|
+
version:
|
|
354
|
+
resolvedVersion !== "unknown"
|
|
355
|
+
? resolvedVersion
|
|
356
|
+
: existingMeta.version || "unknown",
|
|
357
|
+
sha256: finalSha256,
|
|
358
|
+
patches,
|
|
359
|
+
appliedAt: new Date().toISOString(),
|
|
360
|
+
backups,
|
|
361
|
+
};
|
|
362
|
+
fs.writeFileSync(
|
|
363
|
+
state.patchJsonPath,
|
|
364
|
+
`${JSON.stringify(nextMeta, null, 2)}\n`,
|
|
365
|
+
"utf8",
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
ok: true,
|
|
370
|
+
status: "patched",
|
|
371
|
+
changed: true,
|
|
372
|
+
binaryPatched: true,
|
|
373
|
+
previousStatus: state.status,
|
|
374
|
+
path: state.path,
|
|
375
|
+
patchJsonPath: state.patchJsonPath,
|
|
376
|
+
sha256: finalSha256,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
2
379
|
|
|
3
380
|
export function commandExists(name) {
|
|
4
381
|
try {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { readFileSync } from "node:fs";
|
|
6
6
|
import process from "node:process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { isJevEnabled, judgeMcpSelection } from "../../hub/lib/jev-addon.mjs";
|
|
8
9
|
import { CORE_SERVERS, readManifest } from "./mcp-manifest.mjs";
|
|
9
10
|
import {
|
|
10
11
|
DOMAIN_TAG_KEYWORDS,
|
|
@@ -639,7 +640,11 @@ export function getCodexMcpConfig(options = {}) {
|
|
|
639
640
|
if (registeredServers.length === 0) {
|
|
640
641
|
return { mcp_servers: {} };
|
|
641
642
|
}
|
|
642
|
-
const allowedServers = new Set(
|
|
643
|
+
const allowedServers = new Set(
|
|
644
|
+
Array.isArray(options.allowedServers)
|
|
645
|
+
? parseAvailableServers(options.allowedServers)
|
|
646
|
+
: resolveAllowedServers(options),
|
|
647
|
+
);
|
|
643
648
|
const resolvedProfile = resolveMcpProfile(
|
|
644
649
|
options.agentType,
|
|
645
650
|
options.requestedProfile,
|
|
@@ -731,9 +736,17 @@ export function buildMcpPolicy(options = {}) {
|
|
|
731
736
|
const hint = buildPromptHint(resolvedOptions);
|
|
732
737
|
|
|
733
738
|
// 플러그인 서버는 transport 설정이 없어 CLI override를 받으면 부팅이 실패한다.
|
|
739
|
+
const configServers = codexServersFromConfig(options.codexConfig);
|
|
740
|
+
const registeredServers =
|
|
741
|
+
configServers.length > 0
|
|
742
|
+
? configServers
|
|
743
|
+
: options.registeredServers !== undefined
|
|
744
|
+
? parseAvailableServers(options.registeredServers)
|
|
745
|
+
: [];
|
|
734
746
|
const codexOptions = {
|
|
735
747
|
...resolvedOptions,
|
|
736
|
-
availableServers:
|
|
748
|
+
availableServers: registeredServers,
|
|
749
|
+
allowedServers,
|
|
737
750
|
};
|
|
738
751
|
|
|
739
752
|
return {
|
|
@@ -749,6 +762,74 @@ export function buildMcpPolicy(options = {}) {
|
|
|
749
762
|
};
|
|
750
763
|
}
|
|
751
764
|
|
|
765
|
+
export async function buildMcpPolicyWithJev(options = {}, jevOptions = {}) {
|
|
766
|
+
const basePolicy = buildMcpPolicy(options);
|
|
767
|
+
if (!isJevEnabled(jevOptions)) return basePolicy;
|
|
768
|
+
const taskText = String(options.taskText || "").trim();
|
|
769
|
+
if (!taskText) return basePolicy;
|
|
770
|
+
|
|
771
|
+
const isAutoProfile =
|
|
772
|
+
!options.requestedProfile || options.requestedProfile === "auto";
|
|
773
|
+
if (!isAutoProfile && options.requestedProfile === "none") {
|
|
774
|
+
return basePolicy;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
const candidateServers = basePolicy.allowedServers;
|
|
778
|
+
if (candidateServers.length === 0) return basePolicy;
|
|
779
|
+
|
|
780
|
+
const selection = await judgeMcpSelection(
|
|
781
|
+
{
|
|
782
|
+
taskText,
|
|
783
|
+
agentType: options.agentType || basePolicy.resolvedProfile,
|
|
784
|
+
availableServers: candidateServers,
|
|
785
|
+
},
|
|
786
|
+
jevOptions,
|
|
787
|
+
);
|
|
788
|
+
if (!selection || !Array.isArray(selection.neededMcpServers))
|
|
789
|
+
return basePolicy;
|
|
790
|
+
|
|
791
|
+
const narrowedAvailable = selection.neededMcpServers.filter((server) =>
|
|
792
|
+
basePolicy.allowedServers.includes(server),
|
|
793
|
+
);
|
|
794
|
+
if (narrowedAvailable.length === 0) {
|
|
795
|
+
return {
|
|
796
|
+
...buildMcpPolicy({
|
|
797
|
+
...options,
|
|
798
|
+
requestedProfile: "none",
|
|
799
|
+
registeredServers:
|
|
800
|
+
options.registeredServers ??
|
|
801
|
+
(options.codexConfig === undefined
|
|
802
|
+
? options.availableServers
|
|
803
|
+
: undefined),
|
|
804
|
+
}),
|
|
805
|
+
requestedProfile: options.requestedProfile || "auto",
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const nextProfile =
|
|
810
|
+
isAutoProfile &&
|
|
811
|
+
selection.mcpProfile &&
|
|
812
|
+
selection.mcpProfile in PROFILE_DEFINITIONS
|
|
813
|
+
? selection.mcpProfile
|
|
814
|
+
: options.requestedProfile;
|
|
815
|
+
|
|
816
|
+
const resolved = buildMcpPolicy({
|
|
817
|
+
...options,
|
|
818
|
+
requestedProfile: nextProfile,
|
|
819
|
+
availableServers: narrowedAvailable,
|
|
820
|
+
registeredServers:
|
|
821
|
+
options.registeredServers ??
|
|
822
|
+
(options.codexConfig === undefined
|
|
823
|
+
? options.availableServers
|
|
824
|
+
: undefined),
|
|
825
|
+
});
|
|
826
|
+
return {
|
|
827
|
+
...resolved,
|
|
828
|
+
enabledServers: resolved.allowedServers,
|
|
829
|
+
requestedProfile: options.requestedProfile || "auto",
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
|
|
752
833
|
function shellEscape(value) {
|
|
753
834
|
return `'${String(value).replace(/'/g, `'"'"'`)}'`;
|
|
754
835
|
}
|
|
@@ -863,7 +944,9 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
863
944
|
|
|
864
945
|
let policy;
|
|
865
946
|
try {
|
|
866
|
-
policy =
|
|
947
|
+
policy = isJevEnabled()
|
|
948
|
+
? await buildMcpPolicyWithJev(args)
|
|
949
|
+
: buildMcpPolicy(args);
|
|
867
950
|
} catch (error) {
|
|
868
951
|
console.error(
|
|
869
952
|
`[mcp-filter] ${error instanceof Error ? error.message : String(error)}`,
|