import { Option, Schema } from "effect"; export const DEFAULT_EXECUTOR_SERVER_ORIGIN = "http://127.0.0.1:4000";export const DEFAULT_EXECUTOR_SERVER_USERNAME = "executor"; export type ExecutorServerConnectionKind = "http" | "desktop-sidecar";export type ExecutorLocalServerKind = "cli-daemon" | "desktop-sidecar" | "foreground"; export type ExecutorServerAuth = | { readonly kind: "basic"; readonly username?: string; readonly password: string; } | { readonly kind: "bearer"; readonly token: string; }; export interface ExecutorServerConnection { readonly kind: ExecutorServerConnectionKind; readonly key: string; readonly origin: string; readonly apiBaseUrl: string; readonly displayName: string; readonly auth?: ExecutorServerAuth;} export interface ExecutorServerConnectionInput { readonly kind?: ExecutorServerConnectionKind; readonly key?: string; readonly origin?: string; readonly apiBaseUrl?: string; readonly displayName?: string; readonly auth?: ExecutorServerAuth;} export interface ExecutorLocalServerManifest { readonly version: 1; readonly kind: ExecutorLocalServerKind; readonly pid: number; readonly startedAt: string; readonly dataDir: string; readonly scopeDir: string | null; readonly connection: ExecutorServerConnection; readonly owner: { readonly client: "cli" | "desktop"; readonly version: string | null; readonly executablePath: string | null; };} const stripTrailingSlash = (value: string): string => value.replace(/\/+$/, ""); const displayNameFromOrigin = (origin: string): string => origin.replace(/^https?:\/\//, "").replace(/\/+$/, ""); export const normalizeExecutorServerOrigin = (raw: string): string => { const trimmed = stripTrailingSlash(raw.trim()); if (!trimmed) return DEFAULT_EXECUTOR_SERVER_ORIGIN; const parsed = new URL(/^https?:\/\//.test(trimmed) ? trimmed : `http://${trimmed}`); if (parsed.pathname === "/api") { parsed.pathname = "/"; } parsed.search = ""; parsed.hash = ""; return stripTrailingSlash(parsed.toString());}; export const apiBaseUrlForServerOrigin = (origin: string): string => `${origin}/api`; export const originFromApiBaseUrl = (raw: string): string => { const parsed = new URL(raw); if (parsed.pathname.endsWith("/api")) { parsed.pathname = parsed.pathname.slice(0, -"/api".length) || "/"; } parsed.search = ""; parsed.hash = ""; return normalizeExecutorServerOrigin(parsed.toString());}; export const normalizeExecutorServerConnection = ( input: ExecutorServerConnectionInput = {},): ExecutorServerConnection => { const origin = normalizeExecutorServerOrigin( input.origin ?? (input.apiBaseUrl ? originFromApiBaseUrl(input.apiBaseUrl) : DEFAULT_EXECUTOR_SERVER_ORIGIN), ); const apiBaseUrl = stripTrailingSlash(input.apiBaseUrl ?? apiBaseUrlForServerOrigin(origin)); const kind = input.kind ?? "http"; return { kind, key: input.key ?? `${kind}:${origin}`, origin, apiBaseUrl, displayName: input.displayName ?? displayNameFromOrigin(origin), ...(input.auth ? { auth: input.auth } : {}), };}; const encodeBasicCredentials = (credentials: string): string | null => { if (typeof globalThis.btoa === "function") { return globalThis.btoa(credentials); } const buffer = ( globalThis as { readonly Buffer?: { readonly from: (value: string) => { readonly toString: (encoding: "base64") => string }; }; } ).Buffer; if (buffer) { return buffer.from(credentials).toString("base64"); } return null;}; export const getExecutorServerAuthorizationHeader = ( connection: ExecutorServerConnection,): string | null => { const auth = connection.auth; if (!auth) return null; if (auth.kind === "bearer") return `Bearer ${auth.token}`; const encoded = encodeBasicCredentials( `${auth.username ?? DEFAULT_EXECUTOR_SERVER_USERNAME}:${auth.password}`, ); return encoded ? `Basic ${encoded}` : null;}; const ExecutorServerAuthJson = Schema.Union([ Schema.Struct({ kind: Schema.Literal("basic"), username: Schema.optional(Schema.String), password: Schema.String, }), Schema.Struct({ kind: Schema.Literal("bearer"), token: Schema.String, }),]); const ExecutorServerConnectionJson = Schema.Struct({ kind: Schema.optional(Schema.Literals(["http", "desktop-sidecar"])), key: Schema.optional(Schema.String), origin: Schema.String, apiBaseUrl: Schema.optional(Schema.String), displayName: Schema.optional(Schema.String), auth: Schema.optional(ExecutorServerAuthJson),}); const ExecutorLocalServerManifestJson = Schema.Struct({ version: Schema.Literal(1), kind: Schema.Literals(["cli-daemon", "desktop-sidecar", "foreground"]), pid: Schema.Number, startedAt: Schema.String, dataDir: Schema.String, scopeDir: Schema.NullOr(Schema.String), connection: ExecutorServerConnectionJson, owner: Schema.Struct({ client: Schema.Literals(["cli", "desktop"]), version: Schema.NullOr(Schema.String), executablePath: Schema.NullOr(Schema.String), }),}); const decodeUnknownJsonOption = Schema.decodeUnknownOption(Schema.UnknownFromJsonString);const decodeExecutorLocalServerManifestJson = Schema.decodeUnknownOption( ExecutorLocalServerManifestJson,); const canNormalizeServerOrigin = (origin: string): boolean => { const trimmed = stripTrailingSlash(origin.trim()); if (!trimmed) return true; return URL.canParse(/^https?:\/\//.test(trimmed) ? trimmed : `http://${trimmed}`);}; export const parseExecutorLocalServerManifest = ( raw: string,): ExecutorLocalServerManifest | null => { const json = decodeUnknownJsonOption(raw); if (Option.isNone(json)) return null; const decoded = decodeExecutorLocalServerManifestJson(json.value); if (Option.isNone(decoded)) return null; const parsed = decoded.value; if ( !Number.isInteger(parsed.pid) || parsed.pid <= 0 || !canNormalizeServerOrigin(parsed.connection.origin) ) { return null; } const connection = normalizeExecutorServerConnection(parsed.connection); return { version: 1, kind: parsed.kind, pid: parsed.pid, startedAt: parsed.startedAt, dataDir: parsed.dataDir, scopeDir: parsed.scopeDir, connection, owner: { client: parsed.owner.client, version: parsed.owner.version, executablePath: parsed.owner.executablePath, }, };}; export const serializeExecutorLocalServerManifest = ( manifest: ExecutorLocalServerManifest,): string => `${JSON.stringify(manifest, null, 2)}\n`;
import { Option, Schema } from "effect";
export const DEFAULT_EXECUTOR_SERVER_ORIGIN = "http://127.0.0.1:4000";
export const DEFAULT_EXECUTOR_SERVER_USERNAME = "executor";
export type ExecutorServerConnectionKind = "http" | "desktop-sidecar";
export type ExecutorLocalServerKind = "cli-daemon" | "desktop-sidecar" | "foreground";
export type ExecutorServerAuth =
| {
readonly kind: "basic";
readonly username?: string;
readonly password: string;
}
| {
readonly kind: "bearer";
readonly token: string;
};
export interface ExecutorServerConnection {
readonly kind: ExecutorServerConnectionKind;
readonly key: string;
readonly origin: string;
readonly apiBaseUrl: string;
readonly displayName: string;
readonly auth?: ExecutorServerAuth;
}
export interface ExecutorServerConnectionInput {
readonly kind?: ExecutorServerConnectionKind;
readonly key?: string;
readonly origin?: string;
readonly apiBaseUrl?: string;
readonly displayName?: string;
readonly auth?: ExecutorServerAuth;
}
export interface ExecutorLocalServerManifest {
readonly version: 1;
readonly kind: ExecutorLocalServerKind;
readonly pid: number;
readonly startedAt: string;
readonly dataDir: string;
readonly scopeDir: string | null;
readonly connection: ExecutorServerConnection;
readonly owner: {
readonly client: "cli" | "desktop";
readonly version: string | null;
readonly executablePath: string | null;
};
}
const stripTrailingSlash = (value: string): string => value.replace(/\/+$/, "");
const displayNameFromOrigin = (origin: string): string =>
origin.replace(/^https?:\/\//, "").replace(/\/+$/, "");
export const normalizeExecutorServerOrigin = (raw: string): string => {
const trimmed = stripTrailingSlash(raw.trim());
if (!trimmed) return DEFAULT_EXECUTOR_SERVER_ORIGIN;
const parsed = new URL(/^https?:\/\//.test(trimmed) ? trimmed : `http://${trimmed}`);
if (parsed.pathname === "/api") {
parsed.pathname = "/";
}
parsed.search = "";
parsed.hash = "";
return stripTrailingSlash(parsed.toString());
};
export const apiBaseUrlForServerOrigin = (origin: string): string => `${origin}/api`;
export const originFromApiBaseUrl = (raw: string): string => {
const parsed = new URL(raw);
if (parsed.pathname.endsWith("/api")) {
parsed.pathname = parsed.pathname.slice(0, -"/api".length) || "/";
}
parsed.search = "";
parsed.hash = "";
return normalizeExecutorServerOrigin(parsed.toString());
};
export const normalizeExecutorServerConnection = (
input: ExecutorServerConnectionInput = {},
): ExecutorServerConnection => {
const origin = normalizeExecutorServerOrigin(
input.origin ??
(input.apiBaseUrl ? originFromApiBaseUrl(input.apiBaseUrl) : DEFAULT_EXECUTOR_SERVER_ORIGIN),
);
const apiBaseUrl = stripTrailingSlash(input.apiBaseUrl ?? apiBaseUrlForServerOrigin(origin));
const kind = input.kind ?? "http";
return {
kind,
key: input.key ?? `${kind}:${origin}`,
origin,
apiBaseUrl,
displayName: input.displayName ?? displayNameFromOrigin(origin),
...(input.auth ? { auth: input.auth } : {}),
};
};
const encodeBasicCredentials = (credentials: string): string | null => {
if (typeof globalThis.btoa === "function") {
return globalThis.btoa(credentials);
}
const buffer = (
globalThis as {
readonly Buffer?: {
readonly from: (value: string) => { readonly toString: (encoding: "base64") => string };
};
}
).Buffer;
if (buffer) {
return buffer.from(credentials).toString("base64");
}
return null;
};
export const getExecutorServerAuthorizationHeader = (
connection: ExecutorServerConnection,
): string | null => {
const auth = connection.auth;
if (!auth) return null;
if (auth.kind === "bearer") return `Bearer ${auth.token}`;
const encoded = encodeBasicCredentials(
`${auth.username ?? DEFAULT_EXECUTOR_SERVER_USERNAME}:${auth.password}`,
);
return encoded ? `Basic ${encoded}` : null;
};
const ExecutorServerAuthJson = Schema.Union([
Schema.Struct({
kind: Schema.Literal("basic"),
username: Schema.optional(Schema.String),
password: Schema.String,
}),
Schema.Struct({
kind: Schema.Literal("bearer"),
token: Schema.String,
}),
]);
const ExecutorServerConnectionJson = Schema.Struct({
kind: Schema.optional(Schema.Literals(["http", "desktop-sidecar"])),
key: Schema.optional(Schema.String),
origin: Schema.String,
apiBaseUrl: Schema.optional(Schema.String),
displayName: Schema.optional(Schema.String),
auth: Schema.optional(ExecutorServerAuthJson),
});
const ExecutorLocalServerManifestJson = Schema.Struct({
version: Schema.Literal(1),
kind: Schema.Literals(["cli-daemon", "desktop-sidecar", "foreground"]),
pid: Schema.Number,
startedAt: Schema.String,
dataDir: Schema.String,
scopeDir: Schema.NullOr(Schema.String),
connection: ExecutorServerConnectionJson,
owner: Schema.Struct({
client: Schema.Literals(["cli", "desktop"]),
version: Schema.NullOr(Schema.String),
executablePath: Schema.NullOr(Schema.String),
}),
});
const decodeUnknownJsonOption = Schema.decodeUnknownOption(Schema.UnknownFromJsonString);
const decodeExecutorLocalServerManifestJson = Schema.decodeUnknownOption(
ExecutorLocalServerManifestJson,
);
const canNormalizeServerOrigin = (origin: string): boolean => {
const trimmed = stripTrailingSlash(origin.trim());
if (!trimmed) return true;
return URL.canParse(/^https?:\/\//.test(trimmed) ? trimmed : `http://${trimmed}`);
};
export const parseExecutorLocalServerManifest = (
raw: string,
): ExecutorLocalServerManifest | null => {
const json = decodeUnknownJsonOption(raw);
if (Option.isNone(json)) return null;
const decoded = decodeExecutorLocalServerManifestJson(json.value);
if (Option.isNone(decoded)) return null;
const parsed = decoded.value;
if (
!Number.isInteger(parsed.pid) ||
parsed.pid <= 0 ||
!canNormalizeServerOrigin(parsed.connection.origin)
) {
return null;
}
const connection = normalizeExecutorServerConnection(parsed.connection);
return {
version: 1,
kind: parsed.kind,
pid: parsed.pid,
startedAt: parsed.startedAt,
dataDir: parsed.dataDir,
scopeDir: parsed.scopeDir,
connection,
owner: {
client: parsed.owner.client,
version: parsed.owner.version,
executablePath: parsed.owner.executablePath,
},
};
};
export const serializeExecutorLocalServerManifest = (
manifest: ExecutorLocalServerManifest,
): string => `${JSON.stringify(manifest, null, 2)}\n`;