@triflux/remote 10.49.0 → 10.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hub/team/claude-daemon-control.mjs +54 -6
- package/hub/team/claude-transcript.mjs +240 -0
- package/hub/team/cli/commands/start/index.mjs +17 -3
- package/hub/team/cli/commands/start/parse-args.mjs +1 -0
- package/hub/team/session-context.mjs +203 -0
- package/hub/team/uds-orchestrator.mjs +63 -1
- package/hub/team/worktree-lifecycle.mjs +0 -1
- package/package.json +1 -1
- package/scripts/lib/doctor-env-checks.mjs +0 -85
- package/scripts/lib/legacy-hook-cleanup.mjs +230 -0
- package/scripts/lib/legacy-mcp-cleanup.mjs +772 -0
- package/scripts/lib/mcp-guard-engine.mjs +44 -271
- package/scripts/lib/mcp-manifest.mjs +2 -2
- package/scripts/lib/cross-review-utils.mjs +0 -59
- package/scripts/lib/hook-utils.mjs +0 -16
- package/scripts/lib/keyword-rules.mjs +0 -296
- package/scripts/lib/mcp-gateway-health-check.mjs +0 -224
- package/scripts/lib/mcp-gateway-servers.mjs +0 -70
- package/scripts/lib/mcp-gateway-wrapper-check.mjs +0 -35
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
chmodSync,
|
|
3
|
-
copyFileSync,
|
|
4
3
|
existsSync,
|
|
5
4
|
mkdirSync,
|
|
6
5
|
readdirSync,
|
|
@@ -22,22 +21,7 @@ const PROJECT_ROOT = fileURLToPath(new URL("../../", import.meta.url));
|
|
|
22
21
|
const DEFAULT_REGISTRY_PATH = join(PROJECT_ROOT, "config", "mcp-registry.json");
|
|
23
22
|
const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1"]);
|
|
24
23
|
const DEFAULT_HUB_PATH = "/mcp";
|
|
25
|
-
const SERVER_POLICIES = new Set([
|
|
26
|
-
"hosted",
|
|
27
|
-
"gateway-sse",
|
|
28
|
-
"gateway-http",
|
|
29
|
-
"stdio",
|
|
30
|
-
]);
|
|
31
|
-
const GATEWAY_PORTS = Object.freeze({
|
|
32
|
-
context7: 8100,
|
|
33
|
-
"brave-search": 8101,
|
|
34
|
-
exa: 8102,
|
|
35
|
-
tavily: 8103,
|
|
36
|
-
jira: 8104,
|
|
37
|
-
serena: 8105,
|
|
38
|
-
notion: 8106,
|
|
39
|
-
"notion-guest": 8107,
|
|
40
|
-
});
|
|
24
|
+
const SERVER_POLICIES = new Set(["hosted", "stdio"]);
|
|
41
25
|
const DEFAULT_REGISTRY = Object.freeze({
|
|
42
26
|
$schema: "mcp-registry-schema",
|
|
43
27
|
version: 1,
|
|
@@ -65,7 +49,6 @@ const DEFAULT_REGISTRY = Object.freeze({
|
|
|
65
49
|
},
|
|
66
50
|
},
|
|
67
51
|
policies: {
|
|
68
|
-
stdio_action: "replace-with-hub",
|
|
69
52
|
unknown_server_action: "warn",
|
|
70
53
|
sync_denylist: [],
|
|
71
54
|
watched_paths: [
|
|
@@ -153,12 +136,6 @@ function registryPath() {
|
|
|
153
136
|
: DEFAULT_REGISTRY_PATH;
|
|
154
137
|
}
|
|
155
138
|
|
|
156
|
-
function ensureBackup(filePath) {
|
|
157
|
-
const backupPath = `${filePath}.bak`;
|
|
158
|
-
copyFileSync(filePath, backupPath);
|
|
159
|
-
return backupPath;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
139
|
function isJsonMcpConfig(filePath) {
|
|
163
140
|
const name = pathBasename(filePath);
|
|
164
141
|
return (
|
|
@@ -484,12 +461,9 @@ function resolveEnvDescriptors(name, serverConfig) {
|
|
|
484
461
|
const envName = descriptor.env;
|
|
485
462
|
const envValue = process.env[envName];
|
|
486
463
|
if (typeof envValue !== "string" || envValue.length === 0) {
|
|
487
|
-
warnings.push(
|
|
488
|
-
`[mcp-guard] ${name}.${envKey} env skipped: env ${envName} is not set`,
|
|
489
|
-
);
|
|
490
|
-
continue;
|
|
464
|
+
warnings.push(`[mcp-guard] ${name}.${envKey} env ${envName} is not set`);
|
|
491
465
|
}
|
|
492
|
-
env[envKey] =
|
|
466
|
+
env[envKey] = "${" + envName + "}";
|
|
493
467
|
}
|
|
494
468
|
|
|
495
469
|
return { descriptors, env, warnings };
|
|
@@ -567,22 +541,6 @@ function hasOwnEntries(value) {
|
|
|
567
541
|
return value && typeof value === "object" && Object.keys(value).length > 0;
|
|
568
542
|
}
|
|
569
543
|
|
|
570
|
-
function redactHeaders(headers = {}) {
|
|
571
|
-
const redacted = {};
|
|
572
|
-
for (const key of Object.keys(headers || {})) {
|
|
573
|
-
redacted[key] = "***REDACTED***";
|
|
574
|
-
}
|
|
575
|
-
return redacted;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function redactServerConfig(config = {}) {
|
|
579
|
-
if (!config || typeof config !== "object") return config;
|
|
580
|
-
return {
|
|
581
|
-
...config,
|
|
582
|
-
...(config.headers ? { headers: redactHeaders(config.headers) } : {}),
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
|
|
586
544
|
function descriptorsFromCodexConfig(config = {}) {
|
|
587
545
|
const descriptors = {};
|
|
588
546
|
if (typeof config.bearer_token_env_var === "string") {
|
|
@@ -691,6 +649,7 @@ function upsertTomlServer(raw, name, config, codex = {}) {
|
|
|
691
649
|
"http_headers",
|
|
692
650
|
"env_http_headers",
|
|
693
651
|
"env",
|
|
652
|
+
"env_vars",
|
|
694
653
|
]);
|
|
695
654
|
const nextManagedLines = [];
|
|
696
655
|
if (typeof config.url === "string") {
|
|
@@ -707,6 +666,9 @@ function upsertTomlServer(raw, name, config, codex = {}) {
|
|
|
707
666
|
}
|
|
708
667
|
const env = formatTomlInlineTable(config.env || {});
|
|
709
668
|
if (env) nextManagedLines.push(`env = ${env}`);
|
|
669
|
+
if (Array.isArray(codex.env_vars) && codex.env_vars.length > 0) {
|
|
670
|
+
nextManagedLines.push(`env_vars = ${formatTomlArray(codex.env_vars)}`);
|
|
671
|
+
}
|
|
710
672
|
if (codex.bearer_token_env_var) {
|
|
711
673
|
nextManagedLines.push(
|
|
712
674
|
`bearer_token_env_var = ${formatTomlString(codex.bearer_token_env_var)}`,
|
|
@@ -795,34 +757,6 @@ function serverPolicy(serverConfig = {}, registry = {}) {
|
|
|
795
757
|
return "hosted";
|
|
796
758
|
}
|
|
797
759
|
|
|
798
|
-
function gatewaySseUrl(name, serverConfig = {}) {
|
|
799
|
-
if (typeof serverConfig.url === "string" && serverConfig.url.trim()) {
|
|
800
|
-
return normalizeUrl(serverConfig.url);
|
|
801
|
-
}
|
|
802
|
-
const port =
|
|
803
|
-
Number(serverConfig.gateway_port || serverConfig.port || 0) ||
|
|
804
|
-
GATEWAY_PORTS[name];
|
|
805
|
-
if (!Number.isFinite(port) || port <= 0) return "";
|
|
806
|
-
return `http://127.0.0.1:${port}/sse`;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
function gatewayHttpUrl(name, serverConfig = {}) {
|
|
810
|
-
if (typeof serverConfig.url === "string" && serverConfig.url.trim()) {
|
|
811
|
-
return normalizeUrl(serverConfig.url);
|
|
812
|
-
}
|
|
813
|
-
const port =
|
|
814
|
-
Number(serverConfig.gateway_port || serverConfig.port || 0) ||
|
|
815
|
-
GATEWAY_PORTS[name];
|
|
816
|
-
if (!Number.isFinite(port) || port <= 0) return "";
|
|
817
|
-
const path =
|
|
818
|
-
typeof serverConfig.gateway_path === "string" &&
|
|
819
|
-
serverConfig.gateway_path.trim()
|
|
820
|
-
? serverConfig.gateway_path.trim()
|
|
821
|
-
: "/mcp";
|
|
822
|
-
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
823
|
-
return `http://127.0.0.1:${port}${normalizedPath}`;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
760
|
function syncDenylistEntries(policy = {}) {
|
|
827
761
|
if (!Array.isArray(policy?.sync_denylist)) return new Set();
|
|
828
762
|
return new Set(
|
|
@@ -848,9 +782,11 @@ export function buildDesiredServerRecord(name, serverConfig, filePath) {
|
|
|
848
782
|
const policy = serverPolicy(serverConfig);
|
|
849
783
|
if (policy === "stdio") {
|
|
850
784
|
const resolvedEnv = resolveEnvDescriptors(name, serverConfig);
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
785
|
+
const codexTarget = isCodexConfig(filePath);
|
|
786
|
+
const envConfig =
|
|
787
|
+
!codexTarget && hasOwnEntries(resolvedEnv.env)
|
|
788
|
+
? { env: resolvedEnv.env }
|
|
789
|
+
: {};
|
|
854
790
|
return {
|
|
855
791
|
name,
|
|
856
792
|
config: {
|
|
@@ -859,19 +795,21 @@ export function buildDesiredServerRecord(name, serverConfig, filePath) {
|
|
|
859
795
|
...envConfig,
|
|
860
796
|
},
|
|
861
797
|
envDescriptors: resolvedEnv.descriptors,
|
|
862
|
-
codex:
|
|
798
|
+
codex: codexTarget
|
|
799
|
+
? {
|
|
800
|
+
env_vars: Object.values(resolvedEnv.descriptors).map(
|
|
801
|
+
(item) => item.env,
|
|
802
|
+
),
|
|
803
|
+
}
|
|
804
|
+
: {},
|
|
863
805
|
warnings: resolvedEnv.warnings,
|
|
864
806
|
};
|
|
865
807
|
}
|
|
866
808
|
|
|
867
809
|
const url =
|
|
868
|
-
|
|
869
|
-
?
|
|
870
|
-
:
|
|
871
|
-
? gatewayHttpUrl(name, serverConfig)
|
|
872
|
-
: serverConfig?.transport === "hub-url"
|
|
873
|
-
? resolveHubUrl()
|
|
874
|
-
: normalizeUrl(serverConfig?.url || "");
|
|
810
|
+
serverConfig?.transport === "hub-url"
|
|
811
|
+
? resolveHubUrl()
|
|
812
|
+
: normalizeUrl(serverConfig?.url || "");
|
|
875
813
|
const basenameValue = pathBasename(filePath);
|
|
876
814
|
const resolvedHeaders = resolveHeaderDescriptors(
|
|
877
815
|
name,
|
|
@@ -882,46 +820,6 @@ export function buildDesiredServerRecord(name, serverConfig, filePath) {
|
|
|
882
820
|
? { headers: resolvedHeaders.headers }
|
|
883
821
|
: {};
|
|
884
822
|
|
|
885
|
-
if (policy === "gateway-http") {
|
|
886
|
-
if (isCodexConfig(filePath)) {
|
|
887
|
-
return {
|
|
888
|
-
name,
|
|
889
|
-
config: { url },
|
|
890
|
-
headerDescriptors: {},
|
|
891
|
-
codex: {},
|
|
892
|
-
warnings: [],
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
return {
|
|
897
|
-
name,
|
|
898
|
-
config: { type: "http", url },
|
|
899
|
-
headerDescriptors: {},
|
|
900
|
-
codex: {},
|
|
901
|
-
warnings: [],
|
|
902
|
-
};
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
if (policy === "gateway-sse") {
|
|
906
|
-
if (isCodexConfig(filePath)) {
|
|
907
|
-
return {
|
|
908
|
-
name,
|
|
909
|
-
config: { url, transport: "sse" },
|
|
910
|
-
headerDescriptors: {},
|
|
911
|
-
codex: {},
|
|
912
|
-
warnings: [],
|
|
913
|
-
};
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
return {
|
|
917
|
-
name,
|
|
918
|
-
config: { type: "sse", url },
|
|
919
|
-
headerDescriptors: {},
|
|
920
|
-
codex: {},
|
|
921
|
-
warnings: [],
|
|
922
|
-
};
|
|
923
|
-
}
|
|
924
|
-
|
|
925
823
|
if (
|
|
926
824
|
basenameValue === ".mcp.json" ||
|
|
927
825
|
isClaudeProjectMcpConfig(filePath) ||
|
|
@@ -1122,7 +1020,6 @@ function scanCodexConfig(filePath) {
|
|
|
1122
1020
|
exists: true,
|
|
1123
1021
|
parseError: null,
|
|
1124
1022
|
servers,
|
|
1125
|
-
// Wave 1/2-B: Codex stdio servers are observed but not auto-remediated.
|
|
1126
1023
|
stdioServers: [],
|
|
1127
1024
|
};
|
|
1128
1025
|
} catch (error) {
|
|
@@ -1305,9 +1202,7 @@ export function validateRegistry(registry) {
|
|
|
1305
1202
|
typeof server.policy !== "string" ||
|
|
1306
1203
|
!SERVER_POLICIES.has(server.policy)
|
|
1307
1204
|
) {
|
|
1308
|
-
errors.push(
|
|
1309
|
-
`registry.servers.${name}.policy must be hosted, gateway-sse, gateway-http, or stdio`,
|
|
1310
|
-
);
|
|
1205
|
+
errors.push(`registry.servers.${name}.policy must be hosted or stdio`);
|
|
1311
1206
|
}
|
|
1312
1207
|
const transport = server.transport || registry.defaults?.transport;
|
|
1313
1208
|
if (!["hub-url", "http", "stdio"].includes(transport)) {
|
|
@@ -1321,19 +1216,6 @@ export function validateRegistry(registry) {
|
|
|
1321
1216
|
) {
|
|
1322
1217
|
errors.push(`registry.servers.${name}.url must be a non-empty string`);
|
|
1323
1218
|
}
|
|
1324
|
-
if (policy === "gateway-sse" || policy === "gateway-http") {
|
|
1325
|
-
const port = Number(server.gateway_port || server.port || 0);
|
|
1326
|
-
const hasGatewayUrl =
|
|
1327
|
-
typeof server.url === "string" && server.url.trim().length > 0;
|
|
1328
|
-
if (
|
|
1329
|
-
!hasGatewayUrl &&
|
|
1330
|
-
(!Number.isInteger(port) || port <= 0 || port > 65535)
|
|
1331
|
-
) {
|
|
1332
|
-
errors.push(
|
|
1333
|
-
`registry.servers.${name}.gateway_port must be a valid TCP port for ${policy}`,
|
|
1334
|
-
);
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
1219
|
if (policy === "stdio" && server.url !== undefined) {
|
|
1338
1220
|
errors.push(`registry.servers.${name}.url is not used for stdio`);
|
|
1339
1221
|
}
|
|
@@ -1520,14 +1402,6 @@ export function validateRegistry(registry) {
|
|
|
1520
1402
|
}
|
|
1521
1403
|
}
|
|
1522
1404
|
}
|
|
1523
|
-
if (
|
|
1524
|
-
registry.policies.stdio_action &&
|
|
1525
|
-
!["replace-with-hub", "warn"].includes(registry.policies.stdio_action)
|
|
1526
|
-
) {
|
|
1527
|
-
errors.push(
|
|
1528
|
-
"registry.policies.stdio_action must be replace-with-hub or warn",
|
|
1529
|
-
);
|
|
1530
|
-
}
|
|
1531
1405
|
}
|
|
1532
1406
|
|
|
1533
1407
|
return errors;
|
|
@@ -1795,101 +1669,6 @@ export function scanForStdioServers(filePath) {
|
|
|
1795
1669
|
return scanConfig(filePath).stdioServers;
|
|
1796
1670
|
}
|
|
1797
1671
|
|
|
1798
|
-
export function remediate(filePath, stdioServers, policy = {}) {
|
|
1799
|
-
const resolvedPath = resolveFilePath(filePath);
|
|
1800
|
-
const offenders = Array.isArray(stdioServers)
|
|
1801
|
-
? stdioServers.filter((server) => server?.name)
|
|
1802
|
-
: [];
|
|
1803
|
-
const action = policy?.stdio_action || "warn";
|
|
1804
|
-
|
|
1805
|
-
if (offenders.length === 0) {
|
|
1806
|
-
return {
|
|
1807
|
-
action: "noop",
|
|
1808
|
-
modified: false,
|
|
1809
|
-
backupPath: null,
|
|
1810
|
-
removedServers: [],
|
|
1811
|
-
warnings: [],
|
|
1812
|
-
};
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
if (action === "warn") {
|
|
1816
|
-
return {
|
|
1817
|
-
action,
|
|
1818
|
-
modified: false,
|
|
1819
|
-
backupPath: null,
|
|
1820
|
-
removedServers: [],
|
|
1821
|
-
warnings: [
|
|
1822
|
-
`[mcp-guard] stdio MCP 감지: ${offenders.map((server) => server.name).join(", ")}`,
|
|
1823
|
-
],
|
|
1824
|
-
};
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
if (isCodexConfig(resolvedPath)) {
|
|
1828
|
-
return {
|
|
1829
|
-
action,
|
|
1830
|
-
modified: false,
|
|
1831
|
-
backupPath: null,
|
|
1832
|
-
removedServers: [],
|
|
1833
|
-
warnings: ["[mcp-guard] Codex TOML 자동 수정은 Wave 2-B 범위 밖입니다."],
|
|
1834
|
-
};
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
const snapshot = scanConfig(resolvedPath);
|
|
1838
|
-
if (snapshot.parseError) {
|
|
1839
|
-
return {
|
|
1840
|
-
action,
|
|
1841
|
-
modified: false,
|
|
1842
|
-
backupPath: null,
|
|
1843
|
-
removedServers: [],
|
|
1844
|
-
warnings: [`[mcp-guard] 설정 파싱 실패: ${snapshot.parseError.message}`],
|
|
1845
|
-
};
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
let backupPath = null;
|
|
1849
|
-
try {
|
|
1850
|
-
if (existsSync(resolvedPath)) backupPath = ensureBackup(resolvedPath);
|
|
1851
|
-
} catch (error) {
|
|
1852
|
-
return {
|
|
1853
|
-
action,
|
|
1854
|
-
modified: false,
|
|
1855
|
-
backupPath: null,
|
|
1856
|
-
removedServers: [],
|
|
1857
|
-
warnings: [`[mcp-guard] 백업 생성 실패: ${error.message}`],
|
|
1858
|
-
};
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
const removals = offenders.map((server) => server.name);
|
|
1862
|
-
const updates = [];
|
|
1863
|
-
let replacement = null;
|
|
1864
|
-
|
|
1865
|
-
if (action === "replace-with-hub") {
|
|
1866
|
-
const registry = policy.registry || loadRegistryOrDefault();
|
|
1867
|
-
const matchingOffender = offenders.find((server) =>
|
|
1868
|
-
Object.hasOwn(registry.servers || {}, server.name),
|
|
1869
|
-
);
|
|
1870
|
-
const [hubServerName, hubServerConfig] = matchingOffender
|
|
1871
|
-
? [matchingOffender.name, registry.servers[matchingOffender.name]]
|
|
1872
|
-
: getHubServerEntry(registry);
|
|
1873
|
-
const desired = buildDesiredServerRecord(
|
|
1874
|
-
hubServerName,
|
|
1875
|
-
hubServerConfig,
|
|
1876
|
-
resolvedPath,
|
|
1877
|
-
);
|
|
1878
|
-
replacement = { name: desired.name, ...redactServerConfig(desired.config) };
|
|
1879
|
-
updates.push(desired);
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
const result = updateJsonConfig(resolvedPath, updates, removals);
|
|
1883
|
-
return {
|
|
1884
|
-
action,
|
|
1885
|
-
modified: result.modified,
|
|
1886
|
-
backupPath,
|
|
1887
|
-
removedServers: removals,
|
|
1888
|
-
replacement,
|
|
1889
|
-
warnings: updates.flatMap((update) => update.warnings || []),
|
|
1890
|
-
};
|
|
1891
|
-
}
|
|
1892
|
-
|
|
1893
1672
|
export function resolveHubUrl() {
|
|
1894
1673
|
const registryState = inspectRegistry();
|
|
1895
1674
|
const registry = registryState.valid
|
|
@@ -1981,13 +1760,7 @@ export function addRegistryServer(name, url, options = {}) {
|
|
|
1981
1760
|
: cloneDefaultRegistry();
|
|
1982
1761
|
const transport =
|
|
1983
1762
|
options.transport || (trimmedName === "tfx-hub" ? "hub-url" : "http");
|
|
1984
|
-
const policy =
|
|
1985
|
-
options.policy ||
|
|
1986
|
-
(transport === "stdio"
|
|
1987
|
-
? "stdio"
|
|
1988
|
-
: options.gateway_port || options.port
|
|
1989
|
-
? "gateway-http"
|
|
1990
|
-
: "hosted");
|
|
1763
|
+
const policy = options.policy || (transport === "stdio" ? "stdio" : "hosted");
|
|
1991
1764
|
|
|
1992
1765
|
registry.servers[trimmedName] = {
|
|
1993
1766
|
policy,
|
|
@@ -2127,26 +1900,6 @@ export function syncRegistryTargets(options = {}) {
|
|
|
2127
1900
|
status: "invalid-config",
|
|
2128
1901
|
message: snapshot.parseError.message,
|
|
2129
1902
|
});
|
|
2130
|
-
continue;
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
if (
|
|
2134
|
-
snapshot.stdioServers.length > 0 &&
|
|
2135
|
-
!isClaudeUserConfig(target.filePath)
|
|
2136
|
-
) {
|
|
2137
|
-
const remediation = remediate(target.filePath, snapshot.stdioServers, {
|
|
2138
|
-
...registry.policies,
|
|
2139
|
-
registry,
|
|
2140
|
-
});
|
|
2141
|
-
actions.push({
|
|
2142
|
-
type: "remediate",
|
|
2143
|
-
filePath: target.filePath,
|
|
2144
|
-
label: target.label,
|
|
2145
|
-
status: remediation.modified ? "updated" : "warning",
|
|
2146
|
-
removedServers: remediation.removedServers,
|
|
2147
|
-
replacement: remediation.replacement || null,
|
|
2148
|
-
warnings: remediation.warnings || [],
|
|
2149
|
-
});
|
|
2150
1903
|
}
|
|
2151
1904
|
}
|
|
2152
1905
|
|
|
@@ -2175,6 +1928,26 @@ export function syncRegistryTargets(options = {}) {
|
|
|
2175
1928
|
if (serverConfig?.safe !== true) continue;
|
|
2176
1929
|
if (!serverAppliesToClient(serverConfig, target.client)) continue;
|
|
2177
1930
|
|
|
1931
|
+
if (
|
|
1932
|
+
isCodexConfig(target.filePath) &&
|
|
1933
|
+
serverPolicy(serverConfig) === "stdio"
|
|
1934
|
+
) {
|
|
1935
|
+
const remappedEnv = Object.entries(
|
|
1936
|
+
normalizeEnvDescriptors(serverConfig.env),
|
|
1937
|
+
).find(([key, descriptor]) => key !== descriptor.env);
|
|
1938
|
+
if (remappedEnv) {
|
|
1939
|
+
actions.push({
|
|
1940
|
+
type: "sync",
|
|
1941
|
+
filePath: target.filePath,
|
|
1942
|
+
label: target.label,
|
|
1943
|
+
status: "warning",
|
|
1944
|
+
server: name,
|
|
1945
|
+
message: `${name}: Codex env_vars는 ${remappedEnv[1].env}를 ${remappedEnv[0]}로 이름 변경할 수 없습니다`,
|
|
1946
|
+
});
|
|
1947
|
+
continue;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
|
|
2178
1951
|
const denyKey = syncDenylistKey(target.client, name);
|
|
2179
1952
|
if (denylist.has(denyKey)) {
|
|
2180
1953
|
const existing = snapshot.servers.find(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// scripts/lib/mcp-manifest.mjs
|
|
2
2
|
// MCP 서버 활성화 매니페스트 — 단일 진실 소스.
|
|
3
|
-
// tfx-setup 위저드가 저장하고,
|
|
3
|
+
// tfx-setup 위저드가 저장하고, MCP 필터가 참조한다.
|
|
4
4
|
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { homedir } from "node:os";
|
|
@@ -13,7 +13,7 @@ export const MANIFEST_PATH = join(
|
|
|
13
13
|
"mcp-enabled.json",
|
|
14
14
|
);
|
|
15
15
|
|
|
16
|
-
/** API 키 불필요
|
|
16
|
+
/** API 키 불필요 서버 (현재 없음) */
|
|
17
17
|
export const CORE_SERVERS = Object.freeze([]);
|
|
18
18
|
|
|
19
19
|
/** 검색 MCP — API 키 필요 */
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
|
|
3
|
-
export const SESSION_TTL_SEC = 30 * 60;
|
|
4
|
-
export const STATE_REL_PATH = join(".triflux", "state", "cross-review.json");
|
|
5
|
-
|
|
6
|
-
export function readStdin() {
|
|
7
|
-
return new Promise((resolve) => {
|
|
8
|
-
let raw = "";
|
|
9
|
-
process.stdin.setEncoding("utf8");
|
|
10
|
-
process.stdin.on("data", (chunk) => {
|
|
11
|
-
raw += chunk;
|
|
12
|
-
});
|
|
13
|
-
process.stdin.on("end", () => resolve(raw));
|
|
14
|
-
process.stdin.on("error", () => resolve(""));
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function parseJson(raw) {
|
|
19
|
-
try {
|
|
20
|
-
return JSON.parse(raw);
|
|
21
|
-
} catch {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function nowSec() {
|
|
27
|
-
return Math.floor(Date.now() / 1000);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function resolveBaseDir(payload) {
|
|
31
|
-
if (typeof payload?.cwd === "string" && payload.cwd.trim())
|
|
32
|
-
return payload.cwd;
|
|
33
|
-
if (typeof payload?.directory === "string" && payload.directory.trim())
|
|
34
|
-
return payload.directory;
|
|
35
|
-
return process.cwd();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function shouldTrackPath(filePath) {
|
|
39
|
-
if (typeof filePath !== "string" || !filePath.trim()) return false;
|
|
40
|
-
|
|
41
|
-
const lower = filePath.toLowerCase();
|
|
42
|
-
if (
|
|
43
|
-
lower.startsWith(".triflux/") ||
|
|
44
|
-
lower.startsWith(".omc/") ||
|
|
45
|
-
lower.startsWith(".claude/")
|
|
46
|
-
)
|
|
47
|
-
return false;
|
|
48
|
-
if (lower === "package-lock.json" || lower.endsWith("/package-lock.json"))
|
|
49
|
-
return false;
|
|
50
|
-
if (/\.(md|lock|yml|yaml)$/i.test(lower)) return false;
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function expectedReviewer(author) {
|
|
55
|
-
if (author === "claude") return "codex";
|
|
56
|
-
if (author === "codex") return "claude";
|
|
57
|
-
if (author === "gemini") return "claude";
|
|
58
|
-
return "";
|
|
59
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export function nudge(message) {
|
|
2
|
-
process.stdout.write(
|
|
3
|
-
JSON.stringify({
|
|
4
|
-
hookSpecificOutput: {
|
|
5
|
-
hookEventName: "PreToolUse",
|
|
6
|
-
additionalContext: message,
|
|
7
|
-
},
|
|
8
|
-
}),
|
|
9
|
-
);
|
|
10
|
-
process.exit(0);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function deny(reason) {
|
|
14
|
-
process.stderr.write(reason);
|
|
15
|
-
process.exit(2);
|
|
16
|
-
}
|