/** * Credential Store Service * * Manages the workspace-level credential registry and per-credential * encrypted payload files under `~/.crewly/credentials/`. * * The store is split into two surfaces: * * - **Metadata API** (`listCredentials`, `getCredential`, `findCredentialsByProvider`): * returns `CredentialRegistryEntry` objects containing only non-secret * information (id, name, provider, scopes, email, timestamps). Safe to * expose to agent / LLM context and over MCP. * * - **Payload API** (`getPayload`, `setPayload`): decrypts the per-credential * `.enc` file to return the actual token value(s). **Intended only for * credential helpers and the skill executor** (which injects values into * skill subprocess env vars). Callers must never return payload contents * to agent/LLM context. * * Encrypted file format and threat model: see `utils/encryption.utils.ts`. * * @module services/credential/credential-store.service */ import { CredentialRegistryEntry, CredentialPayload, GoogleOAuthPayload, CredentialType, CredentialHelperName, CredentialStatus } from '../../types/credential.types.js'; /** * Arguments for adding an API key credential. */ export interface AddApiKeyInput { /** User-provided display name (must be unique recommended, not enforced). */ name: string; /** Provider identifier (e.g., "gemini", "openai"). */ provider: string; /** The actual API key value — never logged or returned to agent. */ value: string; } /** * Arguments for adding an OAuth credential (usually produced by a helper's * capture flow). */ export interface AddOAuthInput { /** User-provided display name. */ name: string; /** Provider identifier (e.g., "google"). */ provider: string; /** Which helper manages refresh for this credential. */ helper: CredentialHelperName; /** Full OAuth token payload captured from the helper's login flow. */ payload: GoogleOAuthPayload; } /** * Updatable metadata fields on a credential. Other fields are managed * internally (id, type, provider, encFile, createdAt). */ export interface UpdateCredentialMetadata { name?: string; status?: CredentialStatus; lastUsedAt?: string; expiresAt?: number; scopes?: string[]; accountEmail?: string; } /** * Options for constructing a `CredentialStoreService`. Intended mainly for * tests — production uses the default paths under `~/.crewly/credentials/`. */ export interface CredentialStoreOptions { /** Override the credentials directory (default: `~/.crewly/credentials`). */ dir?: string; /** Override the master.key file path (default: `/master.key`). */ masterKeyPath?: string; } /** * File-based credential store. */ export declare class CredentialStoreService { private readonly dir; private readonly registryPath; private readonly masterKeyPath; private initialized; constructor(options?: CredentialStoreOptions); /** * Ensure the credentials directory and master.key exist. Called * automatically by every public method — safe to invoke explicitly * for eager initialization. */ init(): Promise; /** * List all credentials' metadata. Does not include secret values. */ listCredentials(): Promise; /** * Get a single credential's metadata by id. * * @throws CredentialNotFoundError if no entry with that id exists */ getCredential(id: string): Promise; /** * Find all credentials for a given provider, optionally filtered by type. */ findCredentialsByProvider(provider: string, type?: CredentialType): Promise; /** * Add a new API key credential. The value is encrypted on disk and * never exposed back through the metadata API. */ addApiKey(input: AddApiKeyInput): Promise; /** * Add a new OAuth credential. Typically called by a helper's capture * flow after completing an OAuth exchange. */ addOAuth(input: AddOAuthInput): Promise; /** * Update a credential's metadata. Does not touch the encrypted payload. * Use `setPayload` to rewrite the secret material (e.g., after refresh). * * @throws CredentialNotFoundError if no entry with that id exists */ updateCredential(id: string, patch: UpdateCredentialMetadata): Promise; /** * Delete a credential (removes registry entry and encrypted payload). * The registry removal is authoritative — if the `.enc` file is missing * for any reason, that is not an error. * * @throws CredentialNotFoundError if no entry with that id exists */ deleteCredential(id: string): Promise; /** * Decrypt and return the credential's payload. **Do not return the * result to agent / LLM contexts** — intended only for helpers * refreshing tokens and the skill executor injecting env vars. * * @throws CredentialNotFoundError if no entry with that id exists */ getPayload(id: string): Promise; /** * Encrypt and write a new payload for an existing credential. Used by * helpers after a successful token refresh. * * @throws CredentialNotFoundError if no entry with that id exists */ setPayload(id: string, payload: CredentialPayload): Promise; private readRegistry; private writeRegistry; private appendEntry; private encFilePath; private writePayload; private newId; } /** * Get the process-wide CredentialStoreService singleton. */ export declare function getCredentialStoreService(): CredentialStoreService; /** * Reset the singleton — for tests only. */ export declare function resetCredentialStoreService(): void; /** * Replace the singleton with a specific instance — for tests only. * Allows route/controller tests to point the store at a scratch directory. */ export declare function _setCredentialStoreForTesting(svc: CredentialStoreService): void; //# sourceMappingURL=credential-store.service.d.ts.map