/** * Remote registry client. * * Local registry behavior remains the default. These helpers are opt-in: the * authority and the credential both come from the shared fleet ladder * (lib/fleet-credentials.ts), so a service can expose a compatible registry API * without this package hard-coding anything about where it is deployed. */ import type { SkillMeta } from "./registry.js"; export interface RemoteRegistryOptions { apiUrl?: string; endpoint?: string; timeoutMs?: number; authToken?: string | null; fetchImpl?: (input: string | URL | Request, init?: RequestInit) => Promise; } export declare function getConfiguredApiUrl(env?: Record): string | undefined; /** * Compose one Skills API request URL from an authority and an endpoint. * * The Skills server serves its API under `/api/v1`, so a bare authority gets * `/api/v1` appended — the SAME composition `RemoteSkillsClient` performs * (`${origin}/api/v1/...`). The two sites must agree: they are handed the same * origin by the same resolver. * * A trailing `/skills` is only stripped when the API prefix precedes it * (`.../api/skills`, `.../api/v1/skills`), i.e. when an operator pasted the * full collection base that this package's own error messages print. A BARE * trailing `/skills` is NOT a collection: the default fleet authority is * `https://api.hasna.com/skills`, where `/skills` is the gateway's per-app PATH * PREFIX. Treating that as "the base already names the collection" collapsed * every remote read onto the gateway app root — which answers 404 — so a * correctly credentialled install on the default authority could not run * `skills list` at all, on the plain merge path as well as `--remote`. */ export declare function buildSkillsApiUrl(apiUrl: string, endpoint?: string): string; export declare function parseRemoteRegistryPayload(payload: unknown): SkillMeta[]; export declare function parseRemoteSkillPayload(payload: unknown): SkillMeta; export declare function loadRemoteRegistry(options?: RemoteRegistryOptions): Promise; /** * Merge the authenticated remote registry into a local listing, whenever the * install is pointed at a hosted instance. * * This is the fail-closed (R1) default-read merge: a client configured with an * origin sees the folder UNION cloud in the plain `list`/`search` path, while * every other install keeps today's exact local behavior. * * - Nothing configured, local opted in -> the local list is returned * unchanged and no request is attempted. An install running on this * machine must stay byte-identical to the pre-merge output. * - Nothing configured and NO local opt-in -> this throws, from the shared * ladder (MISSING_API_CREDENTIAL, naming `HASNA_SKILLS_LOCAL` as the * deliberate way out): local mode is opt-in only, and an unconfigured * install is a refusal rather than a silent local listing. * - An authority configured with NO credential -> this throws, from the * shared ladder. It used to return the local half silently, which is the * false green the 2026-09-04 ruling removes: an operator who pointed this * CLI at an instance and lost the key was shown a healthy local listing. * - Credential (+ authority, else the fleet gateway) -> the remote registry is fetched and merged under * the precedence in registry-merge.ts (custom > extension > private > * private-hosted > remote > upstream > official), remote rows tagged * `source: "remote"`. * - A configured, authenticated read that FAILS (auth rejection, HTTP * error, network failure) throws a clear error rather than silently * returning the local half — a silent partial listing would report * success for a union the caller asked to include. * * The explicit `--remote` path stays on loadRemoteRegistry(): an explicit * request has always been fatal on failure, and that contract is unchanged. */ export declare function mergeRemoteRegistry(local: SkillMeta[], options?: RemoteRegistryOptions): Promise; export declare function loadRemoteSkill(name: string, options?: RemoteRegistryOptions): Promise;