135 lines · 5.4 KB · TypeScript
RawDownload
import {  normalizeExecutorServerConnection,  type ExecutorLocalServerManifest,  type ExecutorServerAuth,  type ExecutorServerConnection,} from "@executor-js/sdk/shared";import { canAutoStartLocalDaemonForHost } from "./daemon"; // Bearer is the only credential the CLI derives from the environment: a hosted// API key (`EXECUTOR_API_KEY`) or a local/desktop server's bearer token// (`EXECUTOR_AUTH_TOKEN`). Local servers publish their token in the manifest, so// the env override is mainly for pointing the CLI at a remote instance.export const readCliServerAuth = (  env: Record<string, string | undefined> = process.env,): ExecutorServerAuth | undefined => {  const token = env.EXECUTOR_API_KEY ?? env.EXECUTOR_AUTH_TOKEN;  return token ? { kind: "bearer", token } : undefined;}; export const parseCliExecutorServerConnection = (  baseUrl: string,  env: Record<string, string | undefined> = process.env,): ExecutorServerConnection => {  const connection = normalizeExecutorServerConnection({    origin: baseUrl,  });  return normalizeExecutorServerConnection({    ...connection,    auth: readCliServerAuth(env),  });}; export const withCliServerAuthFallback = (  connection: ExecutorServerConnection,  env: Record<string, string | undefined> = process.env,): ExecutorServerConnection =>  connection.auth    ? connection    : normalizeExecutorServerConnection({        ...connection,        auth: readCliServerAuth(env),      }); export const canAutoStartCliServerConnection = (connection: ExecutorServerConnection): boolean => {  if (connection.kind !== "http") return false;  // A stored credential (basic password, bearer key, or an oauth device-login  // token) means the user is targeting an EXISTING authenticated server, never  // spawn a local daemon for it, even when it happens to be on http://localhost  // (e.g. a hosted server reached over a tunnel, or a local e2e cloud server).  if (connection.auth) return false;  const url = new URL(connection.origin);  return url.protocol === "http:" && canAutoStartLocalDaemonForHost(url.hostname);}; export const profileNameFromConnectionKey = (key: string): string | null =>  key.startsWith("profile:") ? key.slice("profile:".length) : null; // A 401 from the server means the stored credential is stale, revoked, or// missing: a sign-in problem, not a transport problem. Rendered instead of// the raw HTTP client error so the user learns the recovery command, not the// failing endpoint.//// The hint mirrors how the user addressed the server. The common case types// no flags (the default profile, normally executor.sh), and login re-targets// that same default, so the fix is plain `executor login`: profile names are// plumbing and stay invisible. Only an explicit --server / --base-url comes// back in the hint.export const describeUnauthorizedCliServer = (input: {  readonly connection: ExecutorServerConnection;  readonly cliPrefix: string;  readonly target: { readonly baseUrl?: string; readonly serverName?: string };}): string => {  const { connection, cliPrefix, target } = input;  const loginCommand = target.serverName    ? `${cliPrefix} login --server ${target.serverName}`    : target.baseUrl      ? `${cliPrefix} login --base-url ${connection.origin}`      : `${cliPrefix} login`;   if (!connection.auth) {    return [      `${connection.origin} requires authentication, and no credentials are stored for it.`,      `Run \`${loginCommand}\` to sign in.`,    ].join("\n");  }   if (connection.auth.kind === "oauth") {    return [      `You're signed out of ${connection.origin}.`,      `Run \`${loginCommand}\` to sign in again.`,    ].join("\n");  }   const envHint =    connection.auth.kind === "bearer" && !profileNameFromConnectionKey(connection.key)      ? " If the key came from EXECUTOR_API_KEY or EXECUTOR_AUTH_TOKEN, check that value."      : "";  return [    `${connection.origin} rejected the stored ${connection.auth.kind} credentials (401).${envHint}`,    `Run \`${loginCommand}\` to sign in again.`,  ].join("\n");}; export type CliServerConnectionSource =  | "explicit"  | "default-profile"  | "implicit-default"  | "active-local"; export type ActiveLocalServerDecision =  | { readonly kind: "use-requested"; readonly connection: ExecutorServerConnection }  | { readonly kind: "use-active"; readonly connection: ExecutorServerConnection }  | { readonly kind: "conflict"; readonly active: ExecutorLocalServerManifest }; const sameOrigin = (left: string, right: string): boolean =>  normalizeExecutorServerConnection({ origin: left }).origin ===  normalizeExecutorServerConnection({ origin: right }).origin; export const chooseCliServerConnectionWithActiveLocal = (input: {  readonly requested: ExecutorServerConnection;  readonly source: CliServerConnectionSource;  readonly active: ExecutorLocalServerManifest | null;}): ActiveLocalServerDecision => {  if (!input.active) return { kind: "use-requested", connection: input.requested };  if (input.source === "active-local") {    return { kind: "use-active", connection: input.active.connection };  }  if (sameOrigin(input.requested.origin, input.active.connection.origin)) {    return { kind: "use-active", connection: input.active.connection };  }  if (canAutoStartCliServerConnection(input.requested)) {    return { kind: "conflict", active: input.active };  }  return { kind: "use-requested", connection: input.requested };};