import { type RecurringRequest, type RecurringActivation, type RecurringListOptions, type RecurringCapability, type RecurringConsentView, type RecurringOccurrenceView, type RecurringPage } from "./remote-recurring.js"; import { type ListRemoteWorkspaceInvitations, type IssueRemoteWorkspaceInvitation, type ResendRemoteWorkspaceInvitation, type RevokeRemoteWorkspaceInvitation, type AcceptRemoteWorkspaceInvitation } from "./remote-invitations.js"; import { type LeaveRemoteWorkspace, type RemoteWorkspaceLeaveResult } from "./remote-workspace-leave.js"; import { type RemoteWorkspaceContext, type RemoteAccountWorkspaces, type RemoteWorkspaceSession, type RemoteWorkspaceSelectionErrorCode } from "./remote-workspace-selection.js"; import { type RemoteWorkspaceMembersOptions, type RemoteWorkspaceMembersPage } from "./remote-workspace.js"; import { type RemoteWorkspaceMemberErrorCode, type SetRemoteWorkspaceMemberRole, type RemoveRemoteWorkspaceMember, type RemoteWorkspaceMemberRoleResult, type RemoteWorkspaceMemberRemovalResult } from "./remote-workspace.js"; import { type RemoteSkillRunContract } from "./remote-run-contract.js"; import { type RemoteCreditPack, type RemoteRunApproval, type RemoteRunQuote } from "./remote-account.js"; import { type RemoteInputFile, type RemoteInputFileDescriptor } from "./remote-files.js"; import { type RemoteCreditCheckout, type RemoteCreditCheckoutOptions, type RemoteCreditCheckoutErrorCode } from "./remote-credit-checkout.js"; import { type UpdateRemoteProfile, type UpdateRemoteWorkspace } from "./remote-profile.js"; import { type RemoteQuoteUnavailableCode } from "./remote-quote-errors.js"; import { type RemoteSkillsAccess } from "./remote-permissions.js"; export type { RemoteQuoteUnavailableCode } from "./remote-quote-errors.js"; /** * A server that predates this client's pin/tag/incremental-sync routes answered * 404/405 for them. The caller must never mistake that for "no pins" or "empty * listing" — a silently-empty sync would look like success and drop nothing on * the next push. This error is how the version-skew surfaces fail-closed. */ export declare class RemoteRouteUnsupportedError extends Error { readonly path: string; readonly status: number; readonly instance: string; constructor(path: string, status: number, instance: string); } /** Any other non-ok response on the new-route methods, with the status attached. */ export declare class RemoteRequestError extends Error { readonly path: string; readonly status: number; constructor(path: string, status: number, _statusText?: string); } export declare class RemoteSkillLifecycleError extends RemoteRequestError { readonly code?: string; constructor(path: string, status: number, code?: string); } /** Bounded checkout outcome; the key is caller-owned, never copied from a server error. */ export declare class RemoteCreditCheckoutError extends RemoteRequestError { readonly code: RemoteCreditCheckoutErrorCode; readonly requestIdempotencyKey: string; readonly retryAfterSeconds?: number | undefined; constructor(code: RemoteCreditCheckoutErrorCode, status: number, requestIdempotencyKey: string, retryAfterSeconds?: number | undefined); } /** A recognized unavailable quote; status compatibility and client-owned copy. */ export declare class RemoteQuoteUnavailableError extends RemoteRequestError { readonly code: RemoteQuoteUnavailableCode; constructor(path: string, code: RemoteQuoteUnavailableCode); } /** A recognized membership refusal, with fixed text and no server payload. */ export declare class RemoteWorkspaceMemberError extends RemoteRequestError { readonly code: RemoteWorkspaceMemberErrorCode; constructor(path: string, code: RemoteWorkspaceMemberErrorCode); } /** Fixed text for recognized workspace refusals; no reflected server error payload. */ export declare class RemoteWorkspaceSelectionError extends RemoteRequestError { readonly code: RemoteWorkspaceSelectionErrorCode; constructor(path: string, code: RemoteWorkspaceSelectionErrorCode); } /** A recognized unavailable capability; all displayed text is client-owned. */ export declare class RemoteCapabilityUnavailableError extends RemoteRequestError { readonly code: "SUBSCRIPTION_CHECKOUT_UNAVAILABLE"; constructor(); } /** * A remote pin on a skill, matching the hosted-pins wire shape * (`{ slug, pinnedAt, metadata }`). `pinnedAt`/`metadata` are server-reported * and may be absent. */ export interface RemoteSkillVersion { slug: string; version: string; bundleSha256: string; bundleByteSize: number; storageKind?: string; manifest?: Record; createdAt: string; current?: boolean; } export interface RemotePin { slug: string; pinnedAt?: string; metadata?: Record; } /** The minimal per-skill row the pin/tag/updated-since routes serve. */ export interface RemoteSkillSummary { slug: string; name?: string; version?: string; updatedAt?: string; } /** * One page of an incremental listing. `nextCursor` is an opaque continuation * token; null (or an absent field) means the listing is complete. */ export interface UpdatedSincePage { skills: RemoteSkillSummary[]; nextCursor: string | null; } export declare class RemoteSkillsClient { private apiUrl; private apiKey; private capabilities?; constructor(apiKey: string, apiUrl?: string); private request; /** * Fail-closed version-skew guard for the pin/tag/updated-since routes. * * A server that predates these routes answers 404 (unmatched path) or 405 * (unmatched method). Both are surfaced as `RemoteRouteUnsupportedError` — * never as an empty listing, which would read as "no pins / no changes" and * silently desynchronize the caller. Every other non-ok response becomes a * `RemoteRequestError` carrying the status. * * `domainNotFoundCodes` is the one deliberate exception: a route the server * DOES have can 404 for a domain reason (the hosted-pins DELETE answers * `{ code: "PIN_NOT_FOUND" }` when no pin exists). A 404 whose JSON body * carries one of those codes is returned to the caller (status intact) so it * can apply domain semantics instead of misreporting version skew. Every * other 404 — including the dispatcher's `{ code: "NOT_FOUND" }` on a route * the server lacks — still throws `RemoteRouteUnsupportedError`. */ private requestNewRoute; listSkills(): Promise; getSkillMd(slug: string): Promise; getSkill(slug: string): Promise; /** * Raw GET for one skill, with the HTTP status surfaced. Used by the reconcile * re-check (registry-reconcile.ts) so it can distinguish "no such skill" (404) from * "the registry failed to answer" (any other non-success status) instead of treating * both as absent. */ getSkillStatus(slug: string): Promise<{ status: number; body: unknown; }>; /** Low-level admission transport; interactive surfaces use submitQuotedRun. */ submitRun(slug: string, input?: Record, args?: string[], approval?: RemoteRunApproval): Promise; quoteRun(slug: string, input?: Record, args?: string[], files?: RemoteInputFileDescriptor[]): Promise; getCapabilities(options?: { refresh?: boolean; }): Promise; /** Add the single supported publication scope to an existing key, metadata only. */ addSkillPublishScope(keyId: string, expectedScopes: string[], expectedOrgId: string): Promise>; /** Quote first and fail closed when the caller has not approved the required credits. */ submitQuotedRun(slug: string, input?: Record, args?: string[], approval?: RemoteRunApproval): Promise; getIdentity(): Promise>; /** List memberships with the current interactive session; never writes credentials. */ listAccountWorkspaces(expectedUserId?: string): Promise; /** Return a new ephemeral session; this client and any saved key/profile stay unchanged. */ switchWorkspace(context: RemoteWorkspaceContext): Promise; private requestWorkspaceSelection; /** Requires a customer session; API keys and support impersonation cannot edit names. */ updateProfile(input: UpdateRemoteProfile): Promise<{ user: import("./remote-profile.js").RemoteCustomerProfile; }>; /** Owner/admin session only; the current workspace identity and slug stay fixed. */ updateCurrentWorkspace(input: UpdateRemoteWorkspace): Promise<{ organization: import("./remote-profile.js").RemoteCurrentWorkspace; }>; /** Current owner/admin customer session only; the server refuses API keys and impersonation. */ listWorkspaceMembers(options?: RemoteWorkspaceMembersOptions): Promise; /** Exact incarnation and expected role; no refresh or retry. Server enforces current authority. */ setWorkspaceMemberRole(membershipId: string, input: SetRemoteWorkspaceMemberRole): Promise; /** Removes only this incarnation. A successful tombstone replay is returned unchanged. */ removeWorkspaceMember(membershipId: string, input: RemoveRemoteWorkspaceMember): Promise; private requestWorkspaceMember; /** Leave only the explicitly confirmed current incarnation, once. No credential writes or retries. */ leaveWorkspace(context: RemoteWorkspaceContext, input: LeaveRemoteWorkspace): Promise; listWorkspaceInvitations(context: RemoteWorkspaceContext, options?: ListRemoteWorkspaceInvitations): Promise; getWorkspaceInvitation(context: RemoteWorkspaceContext, invitationId: string): Promise<{ invitation: import("./remote-invitations.js").RemoteWorkspaceInvitation; }>; issueWorkspaceInvitation(context: RemoteWorkspaceContext, input: IssueRemoteWorkspaceInvitation): Promise; resendWorkspaceInvitation(context: RemoteWorkspaceContext, invitationId: string, input: ResendRemoteWorkspaceInvitation): Promise; revokeWorkspaceInvitation(context: RemoteWorkspaceContext, invitationId: string, input: RevokeRemoteWorkspaceInvitation): Promise; acceptWorkspaceInvitation(context: RemoteWorkspaceContext, invitationId: string, input: AcceptRemoteWorkspaceInvitation): Promise; /** One bounded operation, bound to the observed current incarnation. No retries, * key/session persistence or post-acceptance selection of another workspace. */ private requestWorkspaceInvitation; previewRecurringConsent(request: RecurringRequest, context?: RemoteWorkspaceContext): Promise; getRecurringDraft(draftId: string, context?: RemoteWorkspaceContext): Promise; activateRecurringConsent(draftId: string, approval: RecurringActivation, context?: RemoteWorkspaceContext): Promise; listRecurringConsents(options?: RecurringListOptions, context?: RemoteWorkspaceContext): Promise>; getRecurringConsent(consentId: string, context?: RemoteWorkspaceContext): Promise; listRecurringOccurrences(consentId: string, options?: RecurringListOptions, context?: RemoteWorkspaceContext): Promise>; revokeRecurringConsent(consentId: string, context?: RemoteWorkspaceContext): Promise; /** One explicit operation on a captured connection. No policy inference, * credential persistence, POST retries or replacement idempotency keys. */ private requestRecurring; private dispatchRecurring; listApiKeys(): Promise[]>; createApiKey(name: string, scopes?: string[]): Promise<{ key: string; [field: string]: unknown; }>; revokeApiKey(keyId: string): Promise>; getBillingStatus(): Promise<{ hasPaymentMethod?: boolean | undefined; plan?: string | undefined; creditBalance: number; formattedCreditBalance: string; }>; listCreditPacks(): Promise; /** One checkout POST. Retain an explicit key before calling to recover even a lost process. */ createCreditCheckout(packId: string, options?: RemoteCreditCheckoutOptions): Promise; getUsage(): Promise[]>; listInvoices(): Promise[]>; createBillingCheckout(): Promise<{ url: string; }>; createBillingPortal(): Promise<{ url: string; }>; cancelRun(runId: string): Promise; resumeRun(runId: string): Promise; private controlRun; private checkoutResponse; private arrayResponse; getRun(runId: string): Promise; getRunLogs(runId: string): Promise; listRuns(limit?: number): Promise; getRunArtifacts(runId: string): Promise; downloadRunArtifact(runId: string, artifactId: string): Promise; getVerifiedRunArtifact(runId: string, artifactId: string, maximumBytes?: number): Promise<{ id: string; fileName: string; bytes: Uint8Array; byteSize: number; sha256: any; }>; submitQuotedRunWithFiles(slug: string, input: Record, args: string[], files: RemoteInputFile[], approval?: RemoteRunApproval): Promise; uploadRunFiles(runId: string, files: RemoteInputFile[]): Promise; /** * Publish a skill to the configured instance. * * Sent as multipart rather than as JSON with a base64 field. A base64 body would inflate * the bundle by a third and would have to pass through the server's JSON reader, whose * 1 MB cap exists to keep JSON bodies sane; multipart keeps the tarball on its own path * with its own, larger limit. * * Note the deliberate absence of `request()`: that helper pins * `Content-Type: application/json`, and a multipart body whose Content-Type does not * carry the generated boundary is unparseable at the other end. * * Optimistic concurrency (todos d061fcda): pass the revision id this client last read * for the slug (from getSkill().revisionId) as `ifMatch`. The instance refuses a * publish against a live slug that does not name its current revision with 409 — this * is how a push never silently overwrites a newer remote revision. */ publishSkill(manifest: Record, bundle?: Uint8Array, ifMatch?: string): Promise; deleteSkill(slug: string): Promise; setSkillLifecycle(slug: string, lifecycle: "active" | "archived", options: { reason?: string; replacementSlug?: string; expectedRevisionId: string; }): Promise; downloadSkillBundle(slug: string): Promise; /** * Bundle fetch for the verified-pull path. Returns the raw Response so the caller can * read the X-Skill-Bundle-Sha256 / X-Skill-Bundle-Signature headers, or null when the * instance serves no bundle for this skill (the metadata-only fallback path). */ getBundle(slug: string, version?: string): Promise; /** Every published version of a slug, newest first (hasna/apps#1630). */ listSkillVersions(slug: string): Promise; /** One version's manifest, or null when the slug@version was never published. */ getSkillVersion(slug: string, version: string): Promise; /** List the pins the instance holds for this principal. */ listPins(): Promise; /** * Pin a skill on the instance (upsert — pinning again refreshes it). The * wire contract matches the hosted-pins routes: a PUT with an optional * `{ metadata }` body, answered with the stored pin (`slug`, `pinnedAt`, * `metadata`). */ pin(slug: string, metadata?: Record): Promise; /** * Unpin a skill on the instance. Resolves true when a pin existed and was * deleted; false when the instance has no pin for this slug (its 404 * carries `code: "PIN_NOT_FOUND"` — a domain answer, not version skew). A * bare 404 (route not deployed) still throws `RemoteRouteUnsupportedError`. */ unpin(slug: string): Promise; /** List the tag names the instance serves. */ listTags(): Promise; /** List the skills carrying a tag on the instance. */ skillsByTag(tag: string): Promise; /** * Cursor-based incremental listing of skills updated after `since` (ISO 8601). * Each page carries an opaque `nextCursor`; null means the listing is complete. * This is the feed T9's sync reconciliation verb consumes. */ listUpdatedSince(since: string, options?: { cursor?: string; limit?: number; }): Promise; } /** * The client for the configured instance, or null when this install runs on * this machine — which is now the explicit local opt-in only * (`HASNA_SKILLS_LOCAL=1`); with no credential, no authority and no opt-in the * shared ladder throws (fail-closed ruling), so the caller fails loudly instead * of quietly reading the bundled corpus while authentication is unconfigured. * * A configured authority with no credential also throws for the same reason. * * ASYNC because the credential ladder is: a vault pointer * (`HASNA_SKILLS_API_KEY_REF`) is completed through the secrets vault before a * client is built, so this never hands `RemoteSkillsClient` an empty key to put * behind `Authorization: Bearer `. */ export declare function createRemoteSkillsClient(env?: Record): Promise; /** * Write-free client resolution for read-only paths (e.g. `sync --dry-run`). * * Identical to createRemoteSkillsClient() now that resolution is the shared * ladder, which reads the Keychain and the credentials file per call and writes * nothing. Kept as a separate name so read-only callers keep reading as * read-only, and so the distinction survives if a write ever creeps back in. */ export declare function createRemoteSkillsClientReadOnly(env?: Record): Promise;