trellis
Version:
Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications
148 lines (144 loc) • 4.47 kB
JavaScript
import {
loadVmConfig
} from "./chunk-UZRUP7QW.js";
import {
readConfig
} from "./chunk-JA7AIHRK.js";
// src/mcp/gateway-config.ts
import { existsSync, readFileSync, writeFileSync } from "fs";
import { join, resolve } from "path";
var GATEWAY_CONFIG_FILE = ".trellis-mcp-gateway.json";
function gatewayConfigPath(dir = ".") {
return resolve(join(dir, GATEWAY_CONFIG_FILE));
}
function readGatewayConfig(dir = ".") {
const path = gatewayConfigPath(dir);
if (!existsSync(path)) return null;
try {
return JSON.parse(readFileSync(path, "utf8"));
} catch {
throw new Error(`Failed to parse ${path}: invalid JSON`);
}
}
function writeGatewayConfig(config, dir = ".") {
writeFileSync(
gatewayConfigPath(dir),
JSON.stringify(config, null, 2) + "\n",
"utf8"
);
}
// src/mcp/room-registry.ts
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
import { resolve as resolve2 } from "path";
function normalizeBaseUrl(url) {
return url.replace(/\/$/, "");
}
function roomMcpPathForUrl(baseUrl) {
const env = process.env.TRELLIS_MCP_PATH?.trim();
if (env) return env.startsWith("/") ? env : `/${env}`;
try {
const host = new URL(baseUrl).hostname;
if (/\.sprites\.app$/i.test(host)) return "/trellis/mcp";
} catch {
}
return "/mcp";
}
function toMcpUrl(base) {
const trimmed = normalizeBaseUrl(base);
if (trimmed.endsWith("/mcp") || trimmed.endsWith("/trellis/mcp")) {
return trimmed;
}
return `${trimmed}${roomMcpPathForUrl(trimmed)}`;
}
function parseRegistryJson(raw, source) {
try {
const data = JSON.parse(raw);
const rooms = Array.isArray(data) ? data : data.rooms ?? [];
return rooms.filter((r) => r.name && r.url).map((r) => ({
name: r.name,
url: normalizeBaseUrl(r.url),
mcpUrl: toMcpUrl(normalizeBaseUrl(r.url)),
apiKey: r.apiKey,
source,
deployedAt: r.deployedAt,
active: r.active
}));
} catch {
return [];
}
}
function listRegisteredRooms(opts = {}) {
const configDir = opts.configDir ?? ".";
const byUrl = /* @__PURE__ */ new Map();
const envRooms = process.env.TRELLIS_MCP_GATEWAY_ROOMS?.trim();
if (envRooms) {
for (const room of parseRegistryJson(envRooms, "env")) {
byUrl.set(room.url, room);
}
}
const registryPath = process.env.TRELLIS_ROOM_REGISTRY?.trim() ?? resolve2(configDir, ".trellis-rooms.json");
if (existsSync2(registryPath)) {
try {
const raw = readFileSync2(registryPath, "utf8");
for (const room of parseRegistryJson(raw, "registry")) {
byUrl.set(room.url, room);
}
} catch {
}
}
const vm = loadVmConfig();
for (const [name, sprite] of Object.entries(vm.sprites)) {
if (!sprite.hasTrellis && !sprite.apiKey) continue;
const url = normalizeBaseUrl(sprite.url);
byUrl.set(url, {
name,
url,
mcpUrl: toMcpUrl(url),
apiKey: sprite.apiKey,
source: "vm",
active: vm.activeSprite === name
});
}
const dbConfig = readConfig(configDir);
if (dbConfig?.mode === "remote" && dbConfig.url) {
const url = normalizeBaseUrl(dbConfig.url);
const name = dbConfig.spriteName ?? url.replace(/^https?:\/\//, "").split(".")[0];
if (!byUrl.has(url)) {
byUrl.set(url, {
name,
url,
mcpUrl: toMcpUrl(url),
apiKey: dbConfig.apiKey,
source: "project",
deployedAt: dbConfig.deployedAt,
active: true
});
}
}
return [...byUrl.values()].sort((a, b) => a.name.localeCompare(b.name));
}
function getRegisteredRoom(nameOrUrl, opts) {
const needle = nameOrUrl.trim().toLowerCase();
const rooms = listRegisteredRooms(opts);
return rooms.find(
(r) => r.name.toLowerCase() === needle || r.url.toLowerCase() === needle || r.mcpUrl.toLowerCase() === needle || normalizeBaseUrl(needle).endsWith(r.name.toLowerCase())
) ?? null;
}
function gatewayPublicUrl(opts) {
const fromConfig = readGatewayConfig(opts?.configDir ?? ".")?.publicUrl?.trim();
if (fromConfig) return fromConfig.replace(/\/$/, "");
if (opts?.gatewayPublicUrl?.trim()) {
return opts.gatewayPublicUrl.trim().replace(/\/$/, "");
}
const env = process.env.TRELLIS_MCP_GATEWAY_PUBLIC_URL?.trim();
if (env) return env.replace(/\/$/, "");
return "https://mcp.trellis.computer";
}
export {
gatewayConfigPath,
writeGatewayConfig,
roomMcpPathForUrl,
listRegisteredRooms,
getRegisteredRoom,
gatewayPublicUrl
};