import { assertAccountActive } from "../account-disable";
import { accountDb, stmt, type ScopedStatement } from "../scoped-db";
import { forgetLocator, locate, registerLocator } from "../scoped-db/locators";
import { randomId, sha256Hex, toHex } from "../crypto";
import { accountToken } from "../account-scope";
import { revokeAccessTokens, revokeAccessTokensWhere } from "../access-token-store";
import { PAT_PREFIX } from "../auth";
import { assertBelowCap, CAPS } from "../limits";
import type { TokenPermissions } from "../token-scope";
const VISIBLE_TOKEN_CHARS = 6;
/** OAuth-issued tokens are tracked separately so agents cannot crowd out a user's personal tokens. */
const OAUTH_TOKENS_PER_USER = 100;
export type GrantSpec = {
permissions: TokenPermissions;
allRepos: boolean;
repoIds: string[];
agent: string;
};
export type AgentKind = "registered" | "oauth" | "delegated" | "unregistered";
export type MintedToken = { id: string; token: string; expiresAt: number | null };
function newPat(userId: string): string {
return accountToken(PAT_PREFIX, userId, toHex(crypto.getRandomValues(new Uint8Array(20))));
}
/**
* Issues an ordinary fine-grained personal access token (`access_tokens` + `access_token_repos`) and
* records the agent it belongs to, so OAuth grants inherit every existing permission check unchanged.
*/
export async function mintFineGrainedToken(input: {
userId: string;
name: string;
scopes: string;
grant: GrantSpec;
ttlMs: number | null;
clientId: string | null;