Skip to main content
Glama
zerosync-co

Infrastructure Auto Provisioner

by zerosync-co
utils.ts3.9 kB
import * as path from "node:path"; import xdgAppPaths from "xdg-app-paths"; import TOML from "smol-toml"; import * as jwt from "jsonwebtoken"; import { TRPCClientError } from "@trpc/client"; export function isDirectory(configPath: string) { try { return Deno.statSync(configPath).isDirectory; } catch (_error) { // ignore error return false; } } export function getGlobalConfigPath() { const configDir = xdgAppPaths(".autoprovisioner").config(); return configDir; } const TOML_ERROR_NAME = "TomlError"; const TOML_ERROR_SUFFIX = " at row "; type TomlError = Error & { line: number; col: number; }; export function parseTOML( input: string, file?: string, ): Record<string, unknown> | never { try { // Normalize CRLF to LF to avoid hitting https://github.com/iarna/iarna-toml/issues/33. const normalizedInput = input.replace(/\r\n/g, "\n"); return TOML.parse(normalizedInput); } catch (err) { const { name, message, line, col } = err as TomlError; if (name !== TOML_ERROR_NAME) { throw err; } const text = message.substring(0, message.lastIndexOf(TOML_ERROR_SUFFIX)); const lineText = input.split("\n")[line]; const location = { lineText, line: line + 1, column: col - 1, file, fileText: input, }; throw new Error( `Error parsing TOML: ${text} at ${JSON.stringify(location)}`, ); } } export interface State { accessToken?: AccessToken; } interface AccessToken { value: string; } export let LocalState: State = {}; export function getAuthConfigFilePath() { const configDir = getGlobalConfigPath(); return path.join(configDir, "config", "default.toml"); } export function getAuthTokens() { const configPath = getAuthConfigFilePath(); try { Deno.lstatSync(configPath); } catch (_) { throw new Error(`No config file found at ${configPath}`); } const toml = parseTOML( new TextDecoder().decode(Deno.readFileSync(configPath)), ) as { access_token?: string; }; const { access_token } = toml; if (!access_token) { throw new Error("failed to resolve access token"); } const decoded = jwt.decode(access_token, { complete: true }); if (!decoded || !decoded.payload) { throw new Error("Invalid JWT"); } const exp = decoded.payload.exp; const currentTime = Math.floor(Date.now() / 1000); const isExpired = exp ? currentTime > exp : false; if (isExpired) { throw new Error("Access token has expired"); } LocalState = { accessToken: { value: access_token, }, }; } export interface UserAuthConfig { access_token?: string; } export function writeAuthConfigFile(config: UserAuthConfig) { const configPath = getAuthConfigFilePath(); Deno.mkdirSync(path.dirname(configPath), { recursive: true, }); Deno.writeFileSync( path.join(configPath), new TextEncoder().encode(TOML.stringify(config as Record<string, unknown>)), ); } export function getInitializedLocalState(): Promise<typeof LocalState> { return new Promise((resolve, reject) => { const intervalMs = 1000; const timeoutMs = 60 * 1000; let elapsed = 0; const checkInterval = setInterval(() => { elapsed += intervalMs; if ( LocalState.accessToken?.value ) { clearInterval(checkInterval); resolve(LocalState); } if (elapsed >= timeoutMs) { clearInterval(checkInterval); reject( new Error( "failed to resolve initialized LocalState", ), ); } }, intervalMs); }); } export function getTRPCErrorMessage(e: unknown) { return e instanceof TRPCClientError ? e.message : "Internal Server Error"; } export function getBuildEnvVar(key: string) { const value = Deno.env.get(key); if (!value) { throw new Error(`${key} is undefined`); } return value; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zerosync-co/mcp-server-autoprovisioner'

If you have feedback or need assistance with the MCP directory API, please join our Discord server