/** * Framework-table store for the "connect external agents" feature. * * Two additive tables back the browser **Connect** page and * the OAuth-style **device-code flow** a CLI drives: * * - `mcp_connect_tokens` — one row per minted MCP token. We never store the * token value (it's a signed JWT); only its `jti` so revocation is a * SQL lookup. Revoking sets `revoked_at`; the row is never deleted. * - `mcp_device_codes` — short-lived (10 min) device/user code pairs for * the OAuth 2.0 device-authorization-style CLI flow. Single-use * (`consumed_at`), rate-limited at creation. * * Mirrors `application-state/store.ts`: lazy `ensureTable()` and `getDbExec()`. * Device-code reads propagate connection errors so unreadable cannot look * like an absent authorization. `CREATE TABLE IF NOT EXISTS` only — strictly * additive, never DROP / ALTER (shared prod DB rule). */ import { type DbExec } from "../db/client.js"; export declare const MCP_CONNECT_SCOPE = "mcp-connect"; export declare const MCP_CONNECT_OAUTH_CLIENT_ID = "agent-native-connect"; export declare const DEVICE_CODE_TTL_MS: number; export declare const DEFAULT_TOKEN_TTL_DAYS = 365; export declare const MIN_TOKEN_TTL_DAYS = 1; export declare const MAX_TOKEN_TTL_DAYS = 365; export declare const MAX_SERVICE_TOKEN_TTL_DAYS = 3650; export declare const DEVICE_START_MAX = 20; export declare const DEVICE_START_WINDOW_MS = 60000; export declare const mcpConnectStore: import("../db/store-registry.js").Store; export declare function ensureTable(): Promise; export declare function ensureConnectTables(): Promise; export interface MintedTokenRow { id: string; jti: string; ownerEmail: string; orgId: string | null; label: string | null; createdAt: number | null; lastUsedAt: number | null; revokedAt: number | null; kind: "personal" | "service"; serviceName: string | null; createdBy: string | null; } export declare function serviceIdentityEmail(serviceName: string, orgId: string): string; export declare function isServiceIdentityEmail(email: string | undefined): boolean; export declare function normalizeServiceName(raw: string): string; /** * Persist a record of a minted token. The token value itself (a signed JWT) * is NEVER stored — only its `jti`, so revocation is a cheap SQL lookup. */ export declare function recordMintedToken(params: { jti: string; ownerEmail: string; orgId?: string | null; label?: string | null; kind?: "personal" | "service"; serviceName?: string | null; createdBy?: string | null; }, exec?: DbExec): Promise; export type StoredConnectTokenIdentity = Pick; export type ConnectTokenOrgLookup = ({ status: "found"; } & StoredConnectTokenIdentity) | { status: "revoked"; } | { status: "missing"; } | { status: "unavailable"; }; /** * A connect token's standing, read in one query. `unavailable` covers a read * failure and a malformed row: answering `missing` or `found` instead would * admit a revoked token, or refuse a live one, on a transient error. */ export declare function lookupConnectTokenOrg(jti: string): Promise; export declare function listTokens(ownerEmail: string): Promise; export declare function listOrgServiceTokens(orgId: string): Promise; /** * Revoke an org service token by id, scoped to `orgId` AND `kind = 'service'` * so a caller can never revoke another org's token (or someone's personal * token) through this path. Uses the same `revoked_at` gate * `lookupConnectTokenOrg` checks, so revocation takes effect on the next request like personal * tokens. Idempotent; returns true when a row actually transitioned. */ export declare function revokeOrgServiceToken(orgId: string, id: string): Promise; /** * Revoke every active token of one service in a single statement. Unlike * `listOrgServiceTokens`, a connection error is NOT swallowed: retiring a * principal must fail loudly rather than report "0 revoked". */ export declare function revokeServiceTokensByName(orgId: string, serviceName: string): Promise; /** * Revoke a token, but ONLY if it is owned by `ownerEmail` (the caller). The * `owner_email = ?` predicate is the access scope — a caller can never revoke * another user's token. Idempotent: re-revoking keeps the first timestamp. * Returns true when a row was actually transitioned to revoked. */ export declare function revokeToken(ownerEmail: string, id: string): Promise; /** * Best-effort: stamp `last_used_at` for a token. Swallows all errors — this is * pure telemetry and must never affect the auth path. */ export declare function touchTokenUsed(jti: string): Promise; export interface DeviceCodeRow { deviceCode: string; userCode: string; ownerEmail: string | null; orgId: string | null; status: "pending" | "approved" | "minting" | "consumed" | "expired"; tokenJti: string | null; catalogScope: "full" | null; client?: string | null; purpose?: "agent" | "credential" | null; createdAt: number | null; expiresAt: number | null; consumedAt: number | null; } export declare function createDeviceCode(catalogScope?: "full" | null, connection?: { client?: string; purpose?: "agent" | "credential"; }): Promise; export declare function getDeviceCode(deviceCode: string, exec?: DbExec): Promise; export declare function getDeviceCodeByUserCode(userCode: string, exec?: DbExec): Promise; export declare function approveDeviceCode(userCode: string, ownerEmail: string, orgId: string | null, exec?: DbExec): Promise; export declare function consumeDeviceCode(deviceCode: string, tokenJti: string, exec?: DbExec): Promise; export declare function claimDeviceCodeForMint(deviceCode: string, tokenJti: string, exec?: DbExec): Promise; export declare function finishDeviceCodeMint(deviceCode: string, tokenJti: string, exec?: DbExec): Promise; export declare function releaseDeviceCodeMint(deviceCode: string, tokenJti: string, exec?: DbExec): Promise; export declare function expireDeviceCode(deviceCode: string): Promise;