724 lines · 23.8 KB · TypeScript
RawDownload
import * as Data from "effect/Data";import * as Effect from "effect/Effect";import * as Schema from "effect/Schema";import { HttpClient, HttpTraceContext, type Headers } from "effect/unstable/http";import {  EnvironmentCloudEndpointUnavailableError,  type EnvironmentCloudLinkStateResult,  EnvironmentHttpBadRequestError,  EnvironmentHttpConflictError,  EnvironmentHttpForbiddenError,  EnvironmentHttpInternalServerError,  EnvironmentHttpUnauthorizedError,  EnvironmentId,} from "@t3tools/contracts";import {  RelayEnvironmentConnectScope,  type RelayClientDeviceRecord,  type RelayEnvironmentLinkResponse,  RelayProtectedError,  type RelayClientEnvironmentRecord,  type RelayProtectedError as RelayProtectedErrorType,  type RelayManagedEndpointProviderKind,} from "@t3tools/contracts/relay";import {  exchangeRemoteDpopAccessToken,  fetchRemoteEnvironmentDescriptor,  makeEnvironmentHttpApiClient,  ManagedRelayClient,  ManagedRelayDpopSigner,  type WsRpcClient,} from "@t3tools/client-runtime";import { withRelayClientTracing } from "@t3tools/shared/relayTracing"; import { ensureLocalApi } from "../localApi";import {  getPrimaryEnvironmentConnection,  readEnvironmentConnection,  type SavedEnvironmentRecord,} from "../environments/runtime";import {  readPrimaryEnvironmentDescriptor,  readPrimaryEnvironmentTarget,  resolvePrimaryEnvironmentHttpUrl,} from "../environments/primary";import { withPrimaryEnvironmentRequestInit } from "../environments/primary/requestInit";import { resolveCloudPublicConfig } from "./publicConfig";import {  finishRelayClientInstall,  reportRelayClientInstallProgress,  requestRelayClientInstallConfirmation,} from "./relayClientInstallDialog"; export function normalizeRelayBaseUrl(value: string | null | undefined): string | null {  const trimmed = value?.trim();  if (!trimmed) {    return null;  }  return trimmed.replace(/\/+$/g, "");} function relayUrl(): string | null {  return resolveCloudPublicConfig().relayUrl;} export class CloudEnvironmentLinkError extends Data.TaggedError("CloudEnvironmentLinkError")<{  readonly message: string;  readonly cause?: unknown;}> {} const relayClientRpcError = (message: string) => (cause: unknown) =>  new CloudEnvironmentLinkError({    message,    cause,  }); function ensureRelayClientAvailable(  client: WsRpcClient,): Effect.Effect<void, CloudEnvironmentLinkError> {  return Effect.gen(function* () {    const status = yield* Effect.tryPromise({      try: () => client.cloud.getRelayClientStatus(),      catch: relayClientRpcError("Could not check relay client availability."),    });    if (status.status === "available") return;    if (status.status === "unsupported") {      return yield* new CloudEnvironmentLinkError({        message: `T3 Code cannot install the relay client automatically on ${status.platform}-${status.arch}.`,      });    }     const confirmed = yield* Effect.tryPromise({      try: () => requestRelayClientInstallConfirmation(status.version),      catch: relayClientRpcError("Could not confirm relay client installation."),    });    if (!confirmed) {      return yield* new CloudEnvironmentLinkError({        message: "Relay client installation was cancelled.",      });    }     const installed = yield* Effect.tryPromise({      try: () => client.cloud.installRelayClient(reportRelayClientInstallProgress),      catch: relayClientRpcError("Could not install the relay client."),    }).pipe(Effect.ensuring(Effect.sync(finishRelayClientInstall)));    if (installed.status !== "available") {      return yield* new CloudEnvironmentLinkError({        message:          installed.status === "unsupported"            ? `T3 Code cannot install the relay client automatically on ${installed.platform}-${installed.arch}.`            : "The relay client is still unavailable after installation.",      });    }  });} const isRelayProtectedError = Schema.is(RelayProtectedError);const isEnvironmentCloudApiError = Schema.is(  Schema.Union([    EnvironmentHttpBadRequestError,    EnvironmentHttpUnauthorizedError,    EnvironmentHttpForbiddenError,    EnvironmentHttpConflictError,    EnvironmentHttpInternalServerError,    EnvironmentCloudEndpointUnavailableError,  ]),); function relayProtectedErrorMessage(error: RelayProtectedErrorType): string {  switch (error._tag) {    case "RelayAuthInvalidError":      switch (error.reason) {        case "missing_bearer":        case "invalid_bearer":          return "Relay rejected the cloud session token.";        case "invalid_dpop":          return "Relay rejected the DPoP proof.";        case "not_authorized":          return "Relay rejected the authenticated request.";      }    case "RelayEnvironmentLinkProofExpiredError":      return "Relay rejected an expired environment link proof.";    case "RelayEnvironmentLinkProofInvalidError":      return `Relay rejected the environment link proof (${error.reason}).`;    case "RelayEnvironmentConnectNotAuthorizedError":      return "Relay rejected the environment connection request.";    case "RelayEnvironmentEndpointUnavailableError":      return `Relay could not reach the environment endpoint (${error.reason}).`;    case "RelayEnvironmentEndpointTimedOutError":      return "Relay timed out while contacting the environment endpoint.";    case "RelayEnvironmentLinkFailedError":      return `Relay could not link the environment (${error.reason}).`;    case "RelayEnvironmentLinkUnavailableError":      return `Relay cannot provision the managed endpoint (${error.reason}).`;    case "RelayAgentActivityPublishProofExpiredError":      return "Relay rejected an expired agent activity publish proof.";    case "RelayAgentActivityPublishProofInvalidError":      return `Relay rejected the agent activity publish proof (${error.reason}).`;    case "RelayInternalError":      return `Relay encountered an internal error (${error.reason}, trace ${error.traceId}).`;  }} function decodedRelayClientError(message: string) {  return (cause: unknown) => {    const relayError = findRelayProtectedError(cause);    const detail = relayError ? relayProtectedErrorMessage(relayError) : null;    return new CloudEnvironmentLinkError({      message: detail ? `${message}: ${detail}` : message,      cause,    });  };} function findRelayProtectedError(cause: unknown): RelayProtectedErrorType | null {  if (isRelayProtectedError(cause)) {    return cause;  }  if (typeof cause !== "object" || cause === null) {    return null;  }  return "cause" in cause ? findRelayProtectedError(cause.cause) : null;} function findEnvironmentCloudApiError(cause: unknown): { readonly message: string } | null {  if (isEnvironmentCloudApiError(cause)) {    return cause;  }  if (typeof cause !== "object" || cause === null) {    return null;  }  return "cause" in cause ? findEnvironmentCloudApiError(cause.cause) : null;} const environmentApiError = (message: string) => (cause: unknown) => {  const environmentError = findEnvironmentCloudApiError(cause);  return new CloudEnvironmentLinkError({    message: environmentError      ? `${message.replace(/[.:]$/, "")}: ${environmentError.message}`      : message,    cause,  });}; function endpointOrigin(httpBaseUrl: string) {  const url = new URL(httpBaseUrl);  return {    localHttpHost: "127.0.0.1",    localHttpPort: Number(url.port || (url.protocol === "https:" ? 443 : 80)),  };} const MANAGED_ENDPOINT_PROVIDER_KIND =  "cloudflare_tunnel" satisfies RelayManagedEndpointProviderKind; function ensureLinkedEnvironmentMatches(input: {  readonly expectedEnvironmentId: string;  readonly expectedProviderKind: RelayManagedEndpointProviderKind;  readonly link: RelayEnvironmentLinkResponse;}): Effect.Effect<void, CloudEnvironmentLinkError> {  if (input.link.environmentId !== input.expectedEnvironmentId) {    return new CloudEnvironmentLinkError({      message: "Relay returned credentials for a different environment.",    });  }  if (input.link.endpoint.providerKind !== input.expectedProviderKind) {    return new CloudEnvironmentLinkError({      message: "Relay returned credentials for a different endpoint provider.",    });  }  return Effect.void;} export interface CloudLinkTarget {  readonly environmentId: string;  readonly label: string;  readonly httpBaseUrl: string;  readonly wsBaseUrl: string;} export type CloudLinkState = EnvironmentCloudLinkStateResult; export interface CloudManagedConnection {  readonly environmentId: RelayClientEnvironmentRecord["environmentId"];  readonly label: string;  readonly httpBaseUrl: string;  readonly wsBaseUrl: string;  readonly relayUrl: string;  readonly accessToken: string;  readonly relayTraceHeaders: Headers.Headers;} export function collectCloudLinkTargets(input: {  readonly primary: CloudLinkTarget | null;  readonly saved: ReadonlyArray<CloudLinkTarget>;}): ReadonlyArray<CloudLinkTarget> {  const byId = new Map<string, CloudLinkTarget>();  if (input.primary) {    byId.set(input.primary.environmentId, input.primary);  }  for (const environment of input.saved) {    if (!byId.has(environment.environmentId)) {      byId.set(environment.environmentId, environment);    }  }  return [...byId.values()];} export function readPrimaryCloudLinkTarget(): CloudLinkTarget | null {  const descriptor = readPrimaryEnvironmentDescriptor();  const target = readPrimaryEnvironmentTarget();  if (!descriptor || !target) {    return null;  }  return {    environmentId: descriptor.environmentId,    label: descriptor.label,    httpBaseUrl: target.target.httpBaseUrl,    wsBaseUrl: target.target.wsBaseUrl,  };} export function listManagedCloudEnvironments(input: {  readonly clerkToken: string;}): Effect.Effect<  ReadonlyArray<RelayClientEnvironmentRecord>,  CloudEnvironmentLinkError,  ManagedRelayClient> {  return Effect.gen(function* () {    const configuredRelayUrl = relayUrl();    if (!configuredRelayUrl) {      return yield* new CloudEnvironmentLinkError({        message: "T3CODE_RELAY_URL is not configured.",      });    }    const relayClient = yield* ManagedRelayClient;    return yield* relayClient      .listEnvironments({        clerkToken: input.clerkToken,      })      .pipe(        Effect.mapError(          (cause) =>            new CloudEnvironmentLinkError({              message: "Could not list relay-managed environments.",              cause,            }),        ),      );  });} export function listCloudDevices(input: {  readonly clerkToken: string;}): Effect.Effect<  ReadonlyArray<RelayClientDeviceRecord>,  CloudEnvironmentLinkError,  ManagedRelayClient> {  return Effect.gen(function* () {    if (!relayUrl()) {      return yield* new CloudEnvironmentLinkError({        message: "T3CODE_RELAY_URL is not configured.",      });    }    const relayClient = yield* ManagedRelayClient;    return yield* relayClient.listDevices({ clerkToken: input.clerkToken }).pipe(      Effect.mapError(        (cause) =>          new CloudEnvironmentLinkError({            message: "Could not list cloud devices.",            cause,          }),      ),    );  });} export function connectManagedCloudEnvironment(input: {  readonly clerkToken: string;  readonly environment: RelayClientEnvironmentRecord;  readonly relayUrl?: string;}): Effect.Effect<  CloudManagedConnection,  CloudEnvironmentLinkError,  HttpClient.HttpClient | ManagedRelayClient | ManagedRelayDpopSigner> {  return Effect.gen(function* () {    const configuredRelayUrl = relayUrl();    if (!configuredRelayUrl) {      return yield* new CloudEnvironmentLinkError({        message: "T3CODE_RELAY_URL is not configured.",      });    }    const persistedRelayUrl = normalizeRelayBaseUrl(input.relayUrl);    if (persistedRelayUrl && persistedRelayUrl !== configuredRelayUrl) {      return yield* new CloudEnvironmentLinkError({        message: "The saved environment is linked through a different configured relay.",      });    }    const relayClient = yield* ManagedRelayClient;    const connected = yield* relayClient      .connectEnvironment({        clerkToken: input.clerkToken,        scopes: [RelayEnvironmentConnectScope],        environmentId: input.environment.environmentId,      })      .pipe(        Effect.mapError(          (cause) =>            new CloudEnvironmentLinkError({              message: "Could not connect to relay-managed environment.",              cause,            }),        ),      );    if (connected.environmentId !== input.environment.environmentId) {      return yield* new CloudEnvironmentLinkError({        message: "Relay returned credentials for a different environment.",      });    }    if (      connected.endpoint.httpBaseUrl !== input.environment.endpoint.httpBaseUrl ||      connected.endpoint.wsBaseUrl !== input.environment.endpoint.wsBaseUrl ||      connected.endpoint.providerKind !== input.environment.endpoint.providerKind    ) {      return yield* new CloudEnvironmentLinkError({        message: "Relay returned credentials for a different endpoint.",      });    }    const descriptor = yield* fetchRemoteEnvironmentDescriptor({      httpBaseUrl: connected.endpoint.httpBaseUrl,    }).pipe(      Effect.mapError(        (cause) =>          new CloudEnvironmentLinkError({            message: "Could not read connected environment descriptor.",            cause,          }),      ),    );    if (descriptor.environmentId !== connected.environmentId) {      return yield* new CloudEnvironmentLinkError({        message: "Connected endpoint does not match the selected environment.",      });    }    const signer = yield* ManagedRelayDpopSigner;    const bootstrapProof = yield* signer      .createProof({        method: "POST",        url: new URL("/oauth/token", connected.endpoint.httpBaseUrl).toString(),      })      .pipe(        Effect.mapError(          (cause) =>            new CloudEnvironmentLinkError({              message: "Could not create environment DPoP proof.",              cause,            }),        ),      );    const session = yield* exchangeRemoteDpopAccessToken({      httpBaseUrl: connected.endpoint.httpBaseUrl,      credential: connected.credential,      dpopProof: bootstrapProof,    }).pipe(      Effect.mapError(        (cause) =>          new CloudEnvironmentLinkError({            message: "Could not authorize managed environment.",            cause,          }),      ),    );    return {      environmentId: descriptor.environmentId,      label: descriptor.label,      httpBaseUrl: connected.endpoint.httpBaseUrl,      wsBaseUrl: connected.endpoint.wsBaseUrl,      relayUrl: configuredRelayUrl,      accessToken: session.access_token,      relayTraceHeaders: HttpTraceContext.toHeaders(yield* Effect.currentSpan.pipe(Effect.orDie)),    };  }).pipe(    Effect.withSpan("relay.environment.connect", {      root: true,      attributes: { "relay.environment_id": input.environment.environmentId },    }),    withRelayClientTracing,  );} export function readPrimaryCloudLinkState(): Effect.Effect<  CloudLinkState | null,  CloudEnvironmentLinkError,  HttpClient.HttpClient> {  return Effect.gen(function* () {    if (!readPrimaryCloudLinkTarget()) {      return null;    }    const client = yield* makeEnvironmentHttpApiClient(resolvePrimaryEnvironmentHttpUrl("/"));    return yield* client.connect      .linkState({ headers: {} })      .pipe(        withPrimaryEnvironmentRequestInit,        Effect.mapError(environmentApiError("Could not read environment cloud link state.")),      );  });} export function updatePrimaryCloudPreferences(input: {  readonly publishAgentActivity: boolean;}): Effect.Effect<CloudLinkState, CloudEnvironmentLinkError, HttpClient.HttpClient> {  return Effect.gen(function* () {    const client = yield* makeEnvironmentHttpApiClient(resolvePrimaryEnvironmentHttpUrl("/"));    return yield* client.connect      .preferences({        headers: {},        payload: input,      })      .pipe(        withPrimaryEnvironmentRequestInit,        Effect.mapError(environmentApiError("Could not update environment cloud preferences.")),      );  });} export function unlinkPrimaryEnvironmentFromCloud(input: {  readonly clerkToken: string | null;}): Effect.Effect<void, CloudEnvironmentLinkError, HttpClient.HttpClient | ManagedRelayClient> {  return Effect.gen(function* () {    const target = readPrimaryCloudLinkTarget();    if (!target) {      return yield* new CloudEnvironmentLinkError({        message: "Local environment is not ready yet.",      });    }    const client = yield* makeEnvironmentHttpApiClient(resolvePrimaryEnvironmentHttpUrl("/"));    yield* client.connect

Lines 1–500 of 724.