/** * Credential System Types * * Workspace-level credential store. Credentials (API keys, OAuth tokens) are * decoupled from skills: skills declare slots, users add named credentials, * agents bind specific credentials to slots at execution time. * * Registry entries are safe to show to LLM/agent contexts. Encrypted * payloads (containing real token values) are never exposed to the agent — * they are read only by the credential helper and by the skill executor * for env-var injection into subprocesses. * * @module types/credential.types */ /** * High-level type of credential — determines which payload schema is used * and which helper (if any) owns the refresh lifecycle. */ export type CredentialType = 'api-key' | 'google-oauth'; /** * Lifecycle status of a credential. * * - `active`: usable; refresh tokens are valid * - `revoked`: underlying refresh token no longer works. User must re-authorize. */ export type CredentialStatus = 'active' | 'revoked'; /** * Identifier for the helper responsible for token acquisition and refresh. * * - `gemini-cli-workspace`: piggybacks on user's Gemini CLI Workspace extension (Phase 1) * - `byo-oauth`: user's own registered GCP OAuth client (Phase 2) * - `gcloud-adc`: Google Cloud Application Default Credentials (Phase 2) * - `crewly-official`: Crewly's published OAuth app (Phase 3) */ export type CredentialHelperName = 'gemini-cli-workspace' | 'byo-oauth' | 'gcloud-adc' | 'crewly-official'; /** * Public metadata for a credential. Never contains secret material. * This is the exact shape exposed to agents / LLM contexts. */ export interface CredentialRegistryEntry { /** UUID v4 — stable reference used everywhere (bindings, URLs, logs). */ id: string; /** User-provided display name (e.g., "personal-gmail"). Not a reference. */ name: string; /** Type discriminator — drives payload schema. */ type: CredentialType; /** Provider identifier (e.g., "gemini", "google", "openai"). */ provider: string; /** Helper that manages refresh (required for OAuth types). */ helper?: CredentialHelperName; /** Granted OAuth scopes (OAuth types only). */ scopes?: string[]; /** Email address of the OAuth account — for user-visible display. */ accountEmail?: string; /** ISO-8601 creation timestamp. */ createdAt: string; /** ISO-8601 last metadata update. */ updatedAt: string; /** ISO-8601 last injection into a skill run. */ lastUsedAt?: string; /** Unix epoch ms when the current access token expires (OAuth only). */ expiresAt?: number; /** Current status. */ status: CredentialStatus; /** Relative path to the encrypted payload file (from credentials dir). */ encFile: string; } /** * Registry file format stored at `~/.crewly/credentials/registry.json`. */ export interface CredentialRegistry { schemaVersion: 1; credentials: CredentialRegistryEntry[]; } /** * Payload for a static API key credential. */ export interface ApiKeyPayload { type: 'api-key'; value: string; } /** * Payload for a Google OAuth credential with refreshable access. */ export interface GoogleOAuthPayload { type: 'google-oauth'; accessToken: string; refreshToken: string; tokenType: 'Bearer'; /** Unix epoch ms when accessToken expires. */ expiresAt: number; /** Scopes granted by user consent. */ scopes: string[]; /** Account email (also denormalized into registry entry for display). */ accountEmail?: string; /** OAuth client_id used to mint this grant (required for refresh). */ clientId: string; } /** * Discriminated union over all payload variants. Switch on `type`. */ export type CredentialPayload = ApiKeyPayload | GoogleOAuthPayload; /** * Concrete implementation of a credential helper strategy. * * Helpers are responsible for: * - Returning a valid access token (refreshing if near expiry) * - Recognizing `invalid_grant` / revocation and signalling via thrown error * * Implementations must be idempotent under concurrent calls for the same * credential — the store's refresh manager wraps calls in a per-credential * mutex, but helpers should not assume exclusive access across processes. */ export interface CredentialHelper { readonly name: CredentialHelperName; /** * Return a valid access token for the given credential, refreshing if * within the expiry buffer. * * @throws CredentialRevokedError if the refresh token is no longer valid */ getAccessToken(entry: CredentialRegistryEntry, payload: GoogleOAuthPayload): Promise; } /** * A credential requirement declared in a SKILL.md frontmatter. * Each requirement is a named "slot" the skill will read via an env var * (`CREWLY_CRED_` for api-key, `CREWLY_CRED__*` for OAuth). */ export interface SkillCredentialRequirement { /** Slot name — used verbatim in env var prefix. Alphanumeric + hyphens. */ slot: string; /** Credential type this slot expects. */ type: CredentialType; /** Provider the skill expects (e.g., 'gemini', 'google'). */ provider: string; /** Whether execution fails if no credential is bound to this slot. */ required: boolean; /** UUID of a credential to use when no binding is supplied at runtime. */ default?: string; /** OAuth scopes required for this slot (google-oauth only). */ requiredScopes?: string[]; /** Description shown in UI when selecting a credential for this slot. */ description?: string; } /** * Runtime map from slot name to credential UUID, supplied by the agent in * `execute_skill`. Overrides the skill's `default`. */ export type CredentialBindings = Record; /** * One available credential, surfaced in MissingCredentialError so the * caller can re-invoke with `credentialBindings: { [slot]: id }`. */ export interface AvailableCredentialHint { id: string; name: string; type: CredentialType; status: CredentialStatus; } /** * Thrown when a required slot has neither a binding nor a default. * * The error message now includes the list of available credentials of * the matching provider so the caller can re-invoke with the right * `credentialBindings` map without having to grep `~/.crewly/credentials/` * by hand. Issue #324. */ export declare class MissingCredentialError extends Error { readonly slot: string; readonly provider: string; readonly available: readonly AvailableCredentialHint[]; constructor(slot: string, provider: string, available?: readonly AvailableCredentialHint[]); } /** * Thrown when a bound credential's scopes do not cover the slot's requirements. */ export declare class InsufficientScopeError extends Error { readonly slot: string; readonly required: string[]; readonly granted: string[]; readonly missing: string[]; constructor(slot: string, required: string[], granted: string[]); } /** * Thrown when an OAuth credential's refresh token is no longer valid. * Carries the remediation string that callers (UI / agent) can surface to users. */ export declare class CredentialRevokedError extends Error { readonly credentialId: string; readonly credentialName: string; readonly remediation: string; constructor(credentialId: string, credentialName: string, remediation: string); } /** * Thrown when a referenced credential id doesn't exist in the registry. */ export declare class CredentialNotFoundError extends Error { readonly credentialId: string; constructor(credentialId: string); } //# sourceMappingURL=credential.types.d.ts.map