/**
* 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