"use strict";
/**
* Centralized API client for FastMode API requests
* Uses stored credentials with automatic refresh, or falls back to env var
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getApiUrl = getApiUrl;
exports.getApiConfigAsync = getApiConfigAsync;
exports.getApiConfig = getApiConfig;
exports.isAuthConfigured = isAuthConfigured;
exports.needsAuthentication = needsAuthentication;
exports.getAuthRequiredMessage = getAuthRequiredMessage;
exports.apiRequest = apiRequest;
exports.isApiError = isApiError;
exports.needsAuthError = needsAuthError;
exports.resolveProjectId = resolveProjectId;
exports.externalApiRequest = externalApiRequest;
const credentials_1 = require("./credentials");
/**
* Get API URL from environment or default
*/
function getApiUrl() {
return process.env.FASTMODE_API_URL || 'https://api.fastmode.ai';
}
/**
* Get API configuration - tries stored credentials first, then env var
*/
async function getApiConfigAsync() {
// First, try to get valid stored credentials
const credentials = await (0, credentials_1.getValidCredentials)();
if (credentials) {
return {
apiUrl: getApiUrl(),
authToken: credentials.accessToken,
};
}
// Fall back to environment variable (for backward compatibility)
return {
apiUrl: getApiUrl(),
authToken: process.env.FASTMODE_AUTH_TOKEN || null,
};
}
/**
* Synchronous version - only checks env var (for quick checks)
*/
function getApiConfig() {
return {
apiUrl: getApiUrl(),
authToken: process.env.FASTMODE_AUTH_TOKEN || null,
};
}
/**
* Check if authentication is configured (either stored credentials or env var)
*/
function isAuthConfigured() {
return (0, credentials_1.hasCredentials)() || !!process.env.FASTMODE_AUTH_TOKEN;
}
/**
* Check if we need to trigger the device flow
*/
async function needsAuthentication() {
// Wait for any startup auth that might be in progress
const { waitForAuth } = await Promise.resolve().then(() => __importStar(require('./auth-state')));
await waitForAuth();
const credentials = await (0, credentials_1.getValidCredentials)();
if (credentials)
return false;
if (process.env.FASTMODE_AUTH_TOKEN)
return false;
return true;
}
/**
* Get a helpful message for authentication
*/
function getAuthRequiredMessage() {
return `# Authentication Required
You need to authenticate to use this tool.
**Starting authentication flow...**
A browser window will open for you to log in. If it doesn't open automatically, you'll see a URL to visit.
`;
}
/**
* Make an authenticated API request
* Uses stored credentials with auto-refresh, or falls back to env var
*/
async function apiRequest(endpoint, options = {}) {
const config = await getApiConfigAsync();
if (!config.authToken) {
return {
success: false,
error: 'Not authenticated',
statusCode: 401,
needsAuth: true,
};
}
const url = `${config.apiUrl}${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
const headers = {
'Authorization': `Bearer ${config.authToken}`,
'Content-Type': 'application/json',
};
if (options.tenantId) {
headers['X-Tenant-Id'] = options.tenantId;
}
try {
const response = await fetch(url, {
method: options.method || 'GET',
headers,
body: options.body ? JSON.stringify(options.body) : undefined,
});
const data = await response.json();
if (!response.ok) {
// Check if this is an auth error
if (response.status === 401) {
return {
success: false,
error: 'Authentication expired or invalid',
statusCode: 401,
needsAuth: true,
};
}
return {
success: false,
error: data.error || `API request failed with status ${response.status}`,
statusCode: response.status,
};
}
return data;
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (message.includes('fetch') || message.includes('network') || message.includes('ECONNREFUSED')) {
return {
success: false,
error: `Network error: Unable to connect to ${config.apiUrl}`,
statusCode: 0,
};
}
return {
success: false,
error: message,
statusCode: 0,
};
}
}
/**
* Check if a response is an error
*/
function isApiError(response) {
return (typeof response === 'object' &&
response !== null &&
'success' in response &&
response.success === false);
}
/**
* Check if error requires authentication
*/
function needsAuthError(error) {
return error.needsAuth === true || error.statusCode === 401;
}
/**
* Resolve a project identifier to a tenant ID
* Accepts either a UUID or a project name
*/
async function resolveProjectId(projectIdentifier) {
// Check if it looks like a UUID
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (uuidPattern.test(projectIdentifier)) {
return { tenantId: projectIdentifier };
}
// Look up by name using /api/tenants
const response = await apiRequest('/api/tenants');
if (isApiError(response)) {
return { error: `Failed to look up project: ${response.error}` };
}
const projects = response.data;
// Find by exact name match (case-insensitive)
const match = projects.find(p => p.name.toLowerCase() === projectIdentifier.toLowerCase());
if (match) {
return { tenantId: match.id };
}
// Try partial match
const partialMatch = projects.find(p => p.name.toLowerCase().includes(projectIdentifier.toLowerCase()));
if (partialMatch) {
return { tenantId: partialMatch.id };
}
const availableProjects = projects.map(p => `- ${p.name} (${p.id})`).join('\n');
return {
error: `Project "${projectIdentifier}" not found.\n\nAvailable projects:\n${availableProjects || 'None'}`
};
}
/**
* Make an authenticated external API request
* Uses the /external/* endpoints with tenant context
*/
async function externalApiRequest(tenantId, endpoint, options = {}) {
// External API endpoints are at /external/...
const fullEndpoint = endpoint.startsWith('/external')
? endpoint
: `/external${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
return apiRequest(fullEndpoint, { ...options, tenantId });
}