Files
PaperClipAI/ui/src/api/adapters.ts
T
Devin FoleyandPaperclip e3d8f42e19 fix(ui): recover stalled onboarding connection checks (#19505)
Bound the complete onboarding connection check to two minutes, abort its
requests on timeout, and ignore late results. Keep the saved account
available for retry and resume its matching unfinished setup after refresh.

Verified 242 focused tests, workspace typecheck and build, and the complete
hosted CI check set. Greptile is 5/5 with no unresolved review comments.
Local full-UI timing failure and interrupted CI runner attempts remain
documented in the PR evidence.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-06 10:31:18 -08:00

102 lines
3.8 KiB
TypeScript

/**
* @fileoverview Frontend API client for external adapter management.
*/
import { api, type RequestOptions } from "./client";
/**
* The safe scalar login fields the server projects for an adapter that declares
* an interactive login capability. The projection carries no function member and
* no secret. The form reads it to pick the login flow and the login panel.
*/
export interface AdapterLoginProjection {
panelMode: "displayed_code" | "submitted_browser_code";
timeoutPolicy: "caller_bounded" | "fixed";
}
export interface AdapterCapabilities {
supportsInstructionsBundle: boolean;
supportsSkills: boolean;
supportsLocalAgentJwt: boolean;
requiresMaterializedRuntimeSkills: boolean;
supportsAcp: boolean;
/** Present only when the adapter declares an interactive login capability. */
login?: AdapterLoginProjection;
}
export interface AcpTargetDescriptor {
agentId: string;
skillsMode: "ephemeral" | "unsupported";
prerequisites: {
nodeRange?: string;
packages?: string[];
};
}
export interface AdapterInfo {
/** Exact operator-authorized candidates for the requested company and target. */
candidateHarnesses?: Array<{ agent: "antigravity"; model: string }>;
supportedRunners?: import("@paperclipai/shared").AgentRunner[];
defaultRunner?: import("@paperclipai/shared").AgentRunner;
type: string;
label: string;
source: "builtin" | "external";
modelsCount: number;
loaded: boolean;
disabled: boolean;
capabilities: AdapterCapabilities;
acp?: AcpTargetDescriptor;
/** Installed version (for external npm adapters) */
version?: string;
/** Package name (for external adapters) */
packageName?: string;
/** Whether the adapter was installed from a local path (vs npm). */
isLocalPath?: boolean;
/** True when an external plugin has replaced a built-in adapter of the same type. */
overriddenBuiltin?: boolean;
/** True when the external override for a builtin type is currently paused. */
overridePaused?: boolean;
}
export interface AdapterInstallResult {
type: string;
packageName: string;
version?: string;
installedAt: string;
}
export const adaptersApi = {
/** List all registered adapters (built-in + external). */
list: (context?: { companyId: string; environmentId?: string | null }, options?: RequestOptions) => {
const query = new URLSearchParams();
if (context) {
query.set("companyId", context.companyId);
if (context.environmentId) query.set("environmentId", context.environmentId);
}
return api.get<AdapterInfo[]>(`/adapters${query.size ? `?${query}` : ""}`, options);
},
/** Install an external adapter from npm or a local path. */
install: (params: { packageName: string; version?: string; isLocalPath?: boolean }) =>
api.post<AdapterInstallResult>("/adapters/install", params),
/** Remove an external adapter by type. */
remove: (type: string) => api.delete<{ type: string; removed: boolean }>(`/adapters/${type}`),
/** Enable or disable an adapter (disabled adapters hidden from agent menus). */
setDisabled: (type: string, disabled: boolean) =>
api.patch<{ type: string; disabled: boolean; changed: boolean }>(`/adapters/${type}`, { disabled }),
/** Pause or resume an external override of a builtin type. */
setOverridePaused: (type: string, paused: boolean) =>
api.patch<{ type: string; paused: boolean; changed: boolean }>(`/adapters/${type}/override`, { paused }),
/** Reload an external adapter (bust server + client caches). */
reload: (type: string) =>
api.post<{ type: string; version?: string; reloaded: boolean }>(`/adapters/${type}/reload`, {}),
/** Reinstall an npm-sourced adapter (pulls latest from registry, then reloads). */
reinstall: (type: string) =>
api.post<{ type: string; version?: string; reinstalled: boolean }>(`/adapters/${type}/reinstall`, {}),
};