import type { DiscordCredentialStatus, DiscordCredentialsPatch } from '../discord-credentials.js';
import { type EditorInfo } from '../dashboard/open-in-app.js';
import type { CustomPreset, Preferences } from '../registry.js';
/** The outcome of a {@link savePreferences} write. */
export type SavePreferencesResult = {
    ok: true;
} | {
    ok: false;
    error: string;
};
/** The user's stored dashboard preferences, or `{}` when the read fails. */
export declare function onPreferences(): Promise<Preferences>;
/** Persist the dashboard preferences (sanitized in the store). */
export declare function savePreferences(preferences: Preferences): Promise<SavePreferencesResult>;
/** The outcome of a patch write (#1148): the stored result, so the caller can adopt it. */
export type PatchPreferencesResult<T> = {
    ok: true;
    preferences: T;
} | {
    ok: false;
    error: string;
};
/**
 * Merge the keys the caller changed into the stored preferences (#1148), and hand back what is
 * now stored. The write half of the fix for a stale tab reverting settings it never touched:
 * {@link savePreferences} replaces the whole block, so a client's snapshot overwrote whatever
 * anyone else had changed since it loaded. Returning the merged result also lets the caller
 * adopt the truth it just wrote against, so a tab converges instead of staying stale.
 */
export declare function patchPreferences(patch: Preferences): Promise<PatchPreferencesResult<Preferences>>;
/**
 * A project's shared custom presets (#1025), committed into its `.the-framework/` so they travel
 * with the repo — the team-shared counterpart to the user-tier {@link onPreferences} presets. Read
 * from the project's own checkout, so this resolves the project id to its workspace path rather than
 * touching the home registry. `[]` for an unknown project.
 */
export declare function onProjectPresets(projectId: string): Promise<CustomPreset[]>;
/** Persist a project's shared custom presets into its `.the-framework/` (#1025). */
export declare function saveProjectPresets(projectId: string, presets: CustomPreset[]): Promise<SavePreferencesResult>;
/** The editors installed on this server (#727), for the "Preferred editor" picker. */
export declare function onEditors(): Promise<EditorInfo[]>;
/** Which notification channels the daemon can actually deliver on (#948). */
export interface NotifyChannels {
    /** A webhook is set, so Discord delivery can fire. */
    discordWebhook: boolean;
    /**
     * Where each credential came from (#1095), so the UI can offer an edit for one it stores and
     * say "set on the daemon" for one it cannot touch. An absent key means that credential is
     * not set. Still presence, never a value — nothing here can be turned back into a credential.
     */
    sources: DiscordCredentialStatus;
    /** Whether this host can store a credential at all. */
    editable: boolean;
}
/**
 * Whether the daemon has the Discord credentials (#948/#1095). The toggles are per-user
 * preferences, but delivery needs a webhook / a bot token on the daemon — without this read the
 * dashboard let you switch on a channel that delivers nothing and lit the bell for it.
 *
 * Only presence is reported, never the values, and that is the whole contract: a credential set
 * from the dashboard (#1095) lives daemon-side and is never read back to a browser, so this stays
 * booleans plus where each came from.
 */
export declare function onNotifyChannels(): Promise<NotifyChannels>;
/**
 * Store (or clear) the Discord credentials from the dashboard (#1095) — the step that used to
 * need an edit to the daemon's environment and a restart, which made it the one onboarding step
 * you could not finish in-product.
 *
 * Write-only on purpose: there is no companion read. The value goes daemon-side, and the browser
 * only ever learns that it is there ({@link onNotifyChannels}). The store applies it live, so the
 * bot connects and the watchers start on the save rather than at the next daemon start.
 *
 * The exposure is bounded by the guard the rest of this surface already sits behind: on a
 * non-loopback bind every route requires the shared token (#1051), and anyone through that guard
 * can start runs — strictly more than setting a webhook URL.
 */
export declare function saveDiscordCredentials(patch: DiscordCredentialsPatch): Promise<SavePreferencesResult>;
//# sourceMappingURL=preferences.d.ts.map