import * as AtomHttpApi from "effect/unstable/reactivity/AtomHttpApi";import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http";import * as HttpClientError from "effect/unstable/http/HttpClientError";import { ExecutorApi } from "@executor-js/api/client";import * as Cause from "effect/Cause";import * as Effect from "effect/Effect";import * as Option from "effect/Option";import * as Schema from "effect/Schema"; import { getAuthPassword, getBaseUrl } from "./base-url";import { reportHandledFrontendError } from "./error-reporting"; const isApiClientInfrastructureCause = (cause: Cause.Cause<unknown>): boolean => Option.match(Cause.findErrorOption(cause), { onNone: () => false, onSome: (error) => Schema.isSchemaError(error) || HttpClientError.isHttpClientError(error), }); const reportApiClientInfrastructureCause = (cause: Cause.Cause<unknown>) => Effect.sync(() => { if (!isApiClientInfrastructureCause(cause)) return; reportHandledFrontendError(cause, { surface: "api_client", action: "decode_or_transport", }); }); // ---------------------------------------------------------------------------// Core API client — tools + secrets// --------------------------------------------------------------------------- const electronBasicHeader = (): string | null => { const password = getAuthPassword(); if (!password) return null; if (typeof globalThis.btoa !== "function") return null; // The Electron sidecar uses Basic auth with the literal username "executor" // and a session-generated password injected at preload time. return `Basic ${globalThis.btoa(`executor:${password}`)}`;}; const ExecutorApiClient = AtomHttpApi.Service<"ExecutorApiClient">()("ExecutorApiClient", { api: ExecutorApi, httpClient: FetchHttpClient.layer, transformClient: HttpClient.mapRequest((request) => { let next = HttpClientRequest.prependUrl(request, getBaseUrl()); const basic = electronBasicHeader(); if (basic) { next = HttpClientRequest.setHeader(next, "authorization", basic); } return next; }), transformResponse: (effect) => Effect.tapCause(effect, reportApiClientInfrastructureCause),}); export { ExecutorApiClient };
import * as AtomHttpApi from "effect/unstable/reactivity/AtomHttpApi";
import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http";
import * as HttpClientError from "effect/unstable/http/HttpClientError";
import { ExecutorApi } from "@executor-js/api/client";
import * as Cause from "effect/Cause";
import * as Effect from "effect/Effect";
import * as Option from "effect/Option";
import * as Schema from "effect/Schema";
import { getAuthPassword, getBaseUrl } from "./base-url";
import { reportHandledFrontendError } from "./error-reporting";
const isApiClientInfrastructureCause = (cause: Cause.Cause<unknown>): boolean =>
Option.match(Cause.findErrorOption(cause), {
onNone: () => false,
onSome: (error) => Schema.isSchemaError(error) || HttpClientError.isHttpClientError(error),
});
const reportApiClientInfrastructureCause = (cause: Cause.Cause<unknown>) =>
Effect.sync(() => {
if (!isApiClientInfrastructureCause(cause)) return;
reportHandledFrontendError(cause, {
surface: "api_client",
action: "decode_or_transport",
});
});
// ---------------------------------------------------------------------------
// Core API client — tools + secrets
// ---------------------------------------------------------------------------
const electronBasicHeader = (): string | null => {
const password = getAuthPassword();
if (!password) return null;
if (typeof globalThis.btoa !== "function") return null;
// The Electron sidecar uses Basic auth with the literal username "executor"
// and a session-generated password injected at preload time.
return `Basic ${globalThis.btoa(`executor:${password}`)}`;
};
const ExecutorApiClient = AtomHttpApi.Service<"ExecutorApiClient">()("ExecutorApiClient", {
api: ExecutorApi,
httpClient: FetchHttpClient.layer,
transformClient: HttpClient.mapRequest((request) => {
let next = HttpClientRequest.prependUrl(request, getBaseUrl());
const basic = electronBasicHeader();
if (basic) {
next = HttpClientRequest.setHeader(next, "authorization", basic);
}
return next;
}),
transformResponse: (effect) => Effect.tapCause(effect, reportApiClientInfrastructureCause),
});
export { ExecutorApiClient };