import { customHeadersInterceptor, resolveCustomHeaders } from "../client/cf-access.js"; import { EmDashClient } from "../client/index.js"; import type { Interceptor } from "../client/transport.js"; import { getCredentials, saveCredentials } from "./credentials.js"; export interface ClientArgs { url?: string; token?: string; } /** * Shared connection args for all CLI commands that talk to an EmDash instance. * Spread into each command's `args` definition. */ export const connectionArgs = { url: { type: "string" as const, alias: "u", description: "EmDash instance URL", default: "http://localhost:4321", }, token: { type: "string" as const, alias: "t", description: "Auth token", }, header: { type: "string" as const, alias: "H", description: 'Custom header "Name: Value" (repeatable, or use EMDASH_HEADERS env)', }, json: { type: "boolean" as const, description: "Output as JSON", }, }; /** * Create an EmDashClient from CLI args, env vars, and stored credentials. * * Auth resolution order: * 1. --token flag * 2. EMDASH_TOKEN env var * 3. Stored credentials (~/.config/emdash/auth.json) * 4. Dev bypass (if URL is localhost) * * Custom headers are merged from (in priority order): * 1. Stored credentials (persisted during `emdash login --header`) * 2. EMDASH_HEADERS env var * 3. --header CLI flags */ export function createClientFromArgs(args: ClientArgs): EmDashClient { const baseUrl = args.url || process.env["EMDASH_URL"] || "http://localhost:4321"; let token = args.token || process.env["EMDASH_TOKEN"]; const isLocal = baseUrl.includes("localhost") || baseUrl.includes("127.0.0.1"); const cred = !token ? getCredentials(baseUrl) : null; // Merge custom headers: stored credentials < env var < CLI flags const customHeaders = { ...cred?.customHeaders, ...resolveCustomHeaders(), }; const extraInterceptors: Interceptor[] = []; if (Object.keys(customHeaders).length > 0) { extraInterceptors.push(customHeadersInterceptor(customHeaders)); } // Check stored credentials if no explicit token if (!token && cred) { // Check if access token is expired if (new Date(cred.expiresAt) > new Date()) { token = cred.accessToken; } else { // Token expired — use the refresh interceptor in the client // Pass the refresh token so the client can auto-refresh return new EmDashClient({ baseUrl, token: cred.accessToken, refreshToken: cred.refreshToken, onTokenRefresh: (newAccessToken, expiresIn) => { saveCredentials(baseUrl, { ...cred, accessToken: newAccessToken, expiresAt: new Date(Date.now() + expiresIn * 1000).toISOString(), }); }, interceptors: extraInterceptors, }); } } return new EmDashClient({ baseUrl, token, devBypass: !token && isLocal, interceptors: extraInterceptors, }); }