Index
Functions
getAuthConfig
function getAuthConfig(): AuthConfig
Use getAuthConfig() to retrieve the current user's authentication credentials synchronously — without triggering any keychain prompts or OS-level dialogs.
Reach for this when you need auth credentials in a non-interactive context: CLI startup checks, middleware, or anywhere a blocking keychain dialog would be unacceptable. It's the right call for the majority of auth reads; only switch to getAuthConfigAsync() if you specifically need credentials that were stored in the system keychain.
It resolves credentials in priority order: the SKRYPT_API_KEY environment variable first, then the local auth file on disk. If neither is present, it returns an empty/unauthenticated config rather than throwing.
Returns: An AuthConfig object containing the resolved apiKey and associated email. Pass apiKey as the bearer token in downstream API requests, or check for its presence to gate authenticated-only features.
Heads up
- If your user authenticated via
skrypt loginand their credentials were stored in the system keychain (common on macOS), this function won't see them. UsegetAuthConfigAsync()in that case.- Setting
SKRYPT_API_KEYin the environment always takes precedence over the auth file, which can cause confusion during local development if the variable is set globally in your shell.
Example:
// Inline types — do not import from autodocs
interface AuthConfig {
apiKey: string | null;
email: string | null;
}
// Simulate the resolved auth config as getAuthConfig() would return it
function getAuthConfig(): AuthConfig {
const envKey = process.env.SKRYPT_API_KEY;
if (envKey) {
// Mirrors real behavior: env var wins, email comes from auth file metadata
return {
apiKey: envKey,
email: "dev@example.com",
};
}
// Simulate auth file on disk
const authFileExists = true;
if (authFileExists) {
return {
apiKey: "sk_live_4f8a2b1c9d3e7f6a0b5c8d2e1f4a7b3c",
email: "ada@example.com",
};
}
return { apiKey: null, email: null };
}
try {
// Set env var to simulate a CI/CD environment
process.env.SKRYPT_API_KEY = "sk_live_9e2a1b4c7d0f3e6a8b5c2d1e4f7a0b3c";
const auth = getAuthConfig();
if (!auth.apiKey) {
console.log("Not authenticated — run `skrypt login` or set SKRYPT_API_KEY");
process.exit(1);
}
console.log("Authenticated as:", auth.email);
console.log("API key (truncated):", auth.apiKey.slice(0, 12) + "...");
// => Authenticated as: dev@example.com
// => API key (truncated): sk_live_9e2a...
} catch (err) {
console.error("Failed to read auth config:", err);
}
getAuthConfigAsync
async function getAuthConfigAsync(): Promise<AuthConfig>
Use getAuthConfigAsync to retrieve the current user's authentication credentials before making API calls or running any command that requires authorization.
Call this at the start of any command action that needs to talk to the skrypt API — it handles all credential sources automatically so you don't need to check them yourself.
It resolves credentials by checking three sources in priority order: the SKRYPT_API_KEY environment variable first, then the system keychain, then the local auth file on disk. Whichever source has a valid API key wins, and the result is always the same AuthConfig shape regardless of where the key came from.
Returns
A Promise<AuthConfig> containing the resolved apiKey and associated email. Pass apiKey as the bearer token in your API requests. If no credentials are found in any source, the promise rejects — catch this to prompt the user to run skrypt login.
Heads up
SKRYPT_API_KEYin the environment always wins, even if a different account is stored in the keychain. This is intentional for CI environments, but can cause confusion in local development if the variable is set from a previous session.- The function reads from disk and the system keychain on every call — don't call it in a hot loop. Resolve it once per command invocation and pass the config down.
Example:
// Inline types — do not import from autodocs
interface AuthConfig {
apiKey: string;
email: string;
}
// Mock implementation matching the real resolution order:
// SKRYPT_API_KEY env var → keychain → auth file
async function getAuthConfigAsync(): Promise<AuthConfig> {
const envKey = process.env.SKRYPT_API_KEY;
if (envKey) {
// Env var present — use it, pull email from auth file if available
return { apiKey: envKey, email: "dev@example.com" };
}
// Simulate keychain / auth file fallback
const storedConfig = { apiKey: "sk_live_9aB3xQr7mNpL2wKv", email: "ada@lovelace.io" };
if (!storedConfig.apiKey) {
throw new Error("Not authenticated. Run `skrypt login` to continue.");
}
return storedConfig;
}
async function generateDocs(sourcePath: string) {
try {
const auth: AuthConfig = await getAuthConfigAsync();
console.log(`Authenticated as ${auth.email}`);
const response = await fetch("https://api.skrypt.dev/v1/generate", {
method: "POST",
headers: {
Authorization: `Bearer ${auth.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ source: sourcePath }),
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const result = await response.json();
console.log("Docs generated:", result);
} catch (err) {
if (err instanceof Error && err.message.includes("Not authenticated")) {
console.error("Run `skrypt login` first.");
} else {
throw err;
}
}
}
generateDocs("./src");
// Output:
// Authenticated as ada@lovelace.io
// Docs generated: { jobId: 'job_4Kx9mNpQ', status: 'queued', outputDir: './content/docs' }
saveAuthConfig
async function saveAuthConfig(config: AuthConfig): Promise<void>
Use saveAuthConfig to persist your AI provider credentials so skrypt can authenticate on every subsequent generate run.
Call this once after collecting credentials from the user — typically during an onboarding flow or a skrypt login command. After saving, all skrypt CLI commands will automatically pick up the stored config without requiring credentials to be passed explicitly.
The function creates a locked-down config directory (mode 0700) and attempts to store your API key in the system keychain first. If keychain storage succeeds, the key is kept out of the config file on disk — only a reference is written. If the keychain is unavailable, the key falls back to the config file directly.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
config | AuthConfig | Yes | Credentials and provider settings to persist. Pass at minimum an apiKey to enable AI-powered generation. |
config.apiKey | string | No | Your AI provider API key. Stored in the system keychain when available, keeping it off disk. |
Returns
Returns Promise<void>. After the promise resolves, credentials are available to all skrypt CLI commands — run skrypt generate immediately without any additional setup.
Heads up
- The config directory is created at a user-scoped path with
0700permissions. If your process lacks write access to the home directory, this will throw — ensure you're running with appropriate user permissions. - If the system keychain is unavailable (common in CI environments), the API key is written to disk in plaintext. Avoid calling this in automated pipelines; pass credentials via environment variables instead.
Example:
type AuthConfig = {
apiKey?: string
provider?: string
}
// Inline mock of saveAuthConfig to demonstrate the call pattern
// In real usage: this logic lives inside skrypt's auth module
async function saveAuthConfig(config: AuthConfig): Promise<void> {
if (!config.apiKey) {
throw new Error("apiKey is required to authenticate with an AI provider")
}
// Simulates writing to ~/.skrypt/config and attempting keychain storage
console.log(`[mock] Storing config for provider: ${config.provider ?? "openai"}`)
console.log(`[mock] API key stored in keychain: sk-...${config.apiKey.slice(-4)}`)
}
async function main() {
const config: AuthConfig = {
apiKey: "sk-proj-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0",
provider: "openai",
}
try {
await saveAuthConfig(config)
console.log("Auth config saved — run `skrypt generate ./src -o ./docs` to get started.")
} catch (err) {
console.error("Failed to save auth config:", (err as Error).message)
process.exit(1)
}
}
main()
// Expected output:
// [mock] Storing config for provider: openai
// [mock] API key stored in keychain: sk-...t0
// Auth config saved — run `skrypt generate ./src -o ./docs` to get started.
clearAuth
async function clearAuth(): Promise<void>
Use clearAuth to completely sign out of skrypt by removing all stored credentials from both the system keychain and local auth files.
Reach for this when building a logout flow, resetting credentials after a token rotation, or cleaning up auth state in CI environments between runs.
It removes credentials from two places: the OS keychain (via the system keychain API) and a local auth file on disk. Both are cleared in sequence, so no partial credential state is left behind.
Parameters
None.
Returns
Promise<void> — resolves once all credentials have been deleted. After this resolves, any subsequent skrypt operations that require authentication will prompt for new credentials or fail with an auth error.
Heads up
- If the local auth file doesn't exist,
clearAuthskips it silently — it won't throw if you call it on an already-signed-out session. - Keychain deletion runs first; if it fails, the local file deletion is still attempted. Check your OS keychain permissions if you suspect credentials are persisting after a clear.
Example:
import { existsSync, writeFileSync, unlinkSync, mkdirSync } from 'fs'
import { join } from 'path'
import { homedir } from 'os'
// --- Inline mock of skrypt's auth system ---
const AUTH_FILE = join(homedir(), '.skrypt', 'auth.json')
async function keychainDelete(): Promise<void> {
// Simulates removing the entry from the OS keychain
console.log('[keychain] Deleted skrypt credentials from system keychain')
}
async function clearAuth(): Promise<void> {
await keychainDelete()
if (existsSync(AUTH_FILE)) {
try {
unlinkSync(AUTH_FILE)
console.log(`[auth] Removed local auth file: ${AUTH_FILE}`)
} catch (err) {
console.warn('[auth] Could not remove local auth file:', err)
}
} else {
console.log('[auth] No local auth file found — nothing to remove')
}
}
// --- Simulate a signed-in state, then clear it ---
async function main() {
// Set up a fake auth file to demonstrate clearance
const authDir = join(homedir(), '.skrypt')
mkdirSync(authDir, { recursive: true })
writeFileSync(
AUTH_FILE,
JSON.stringify({ token: 'sk_live_abc123xyz', email: 'dev@example.com' }, null, 2),
{ mode: 0o600 }
)
console.log('Before clearAuth — auth file exists:', existsSync(AUTH_FILE))
try {
await clearAuth()
console.log('After clearAuth — auth file exists:', existsSync(AUTH_FILE))
// Expected output:
// Before clearAuth — auth file exists: true
// [keychain] Deleted skrypt credentials from system keychain
// [auth] Removed local auth file: /Users/you/.skrypt/auth.json
// After clearAuth — auth file exists: false
} catch (err) {
console.error('Failed to clear auth:', err)
}
}
main()
getKeyStorageMethod
async function getKeyStorageMethod(): Promise<'keychain' | 'file' | 'env' | 'none'>
Use getKeyStorageMethod to detect where skrypt's API key is currently stored, so you can display auth status, guide users through key setup, or conditionally prompt for credentials.
Reach for this when building CLI onboarding flows, auth status commands, or any logic that needs to behave differently depending on how the user authenticated.
It checks storage locations in priority order — environment variable first, then system keychain, then a local credentials file — and returns the first match. If no key is found anywhere, it returns 'none'.
Returns
A Promise resolving to one of four string literals:
| Value | Meaning |
|---|---|
'env' | Key is set in the SKRYPT_API_KEY environment variable |
'keychain' | Key is stored in the OS keychain (macOS Keychain, Windows Credential Manager, etc.) |
'file' | Key is stored in a local credentials file (typically ~/.skrypt/auth.json) |
'none' | No API key found — the user needs to authenticate |
Pass the result to your auth status display or use it to decide whether to prompt the user to run skrypt auth login.
Heads up
'env'always wins, even if a keychain or file entry also exists. If you're debugging unexpected auth behavior, check for aSKRYPT_API_KEYenvironment variable first.- Returns
'none'— not an error — when unauthenticated. Your calling code should handle this as a normal "not logged in" state rather than a failure.
Example:
type KeyStorageMethod = 'keychain' | 'file' | 'env' | 'none'
// Simulated implementations of the underlying storage checks
async function checkEnv(): Promise<boolean> {
return !!process.env.SKRYPT_API_KEY
}
async function checkKeychain(): Promise<boolean> {
// In production, this queries the OS keychain
return false
}
async function checkFile(): Promise<boolean> {
const fs = await import('fs')
const path = await import('path')
const os = await import('os')
const authFile = path.join(os.homedir(), '.skrypt', 'auth.json')
if (!fs.existsSync(authFile)) return false
try {
const data = JSON.parse(fs.readFileSync(authFile, 'utf8'))
return !!data.apiKey
} catch {
return false
}
}
async function getKeyStorageMethod(): Promise<KeyStorageMethod> {
if (await checkEnv()) return 'env'
if (await checkKeychain()) return 'keychain'
if (await checkFile()) return 'file'
return 'none'
}
const STATUS_MESSAGES: Record<KeyStorageMethod, string> = {
env: '✓ Authenticated via SKRYPT_API_KEY environment variable',
keychain: '✓ Authenticated via system keychain',
file: '✓ Authenticated via credentials file (~/.skrypt/auth.json)',
none: '✗ Not authenticated — run `skrypt auth login` to get started',
}
async function printAuthStatus() {
try {
const method = await getKeyStorageMethod()
console.log(STATUS_MESSAGES[method])
if (method === 'none') {
process.exit(1)
}
} catch (err) {
console.error('Failed to check auth status:', err)
process.exit(1)
}
}
printAuthStatus()
// Example output (when SKRYPT_API_KEY is set):
// ✓ Authenticated via SKRYPT_API_KEY environment variable
checkPlan
async function checkPlan(apiKey: string): Promise<PlanCheckResponse>
Use checkPlan to verify an API key and retrieve the subscription plan details associated with a skrypt account.
Call this before running generation tasks when you need to confirm a key is valid, check plan limits, or gate features based on the user's subscription tier.
It makes an authenticated request to the skrypt API and returns the plan details bound to that key — useful for surfacing plan information in tooling, CI pipelines, or custom skrypt integrations.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
apiKey | string | Yes | A skrypt API key — find yours at your skrypt dashboard. An invalid or expired key causes the request to throw. |
Returns
Returns a Promise<PlanCheckResponse> that resolves to an object describing the plan tied to the key (e.g. plan name, limits, feature flags). Use the resolved value to decide whether to proceed with generation, display plan info to the user, or enforce usage limits before making further API calls.
Heads up
- The promise rejects if the API key is invalid, expired, or the network request fails — always wrap in
try/catchto handle auth errors gracefully. - This hits the network on every call; cache the result if you're checking the plan repeatedly within the same process.
Example:
const API_BASE = "https://api.skrypt.ai";
interface PlanCheckResponse {
plan: "free" | "pro" | "enterprise";
generationsUsed: number;
generationsLimit: number;
features: {
multiLanguage: boolean;
byTopic: boolean;
customTemplates: boolean;
};
}
async function checkPlan(apiKey: string): Promise<PlanCheckResponse> {
const response = await fetch(`${API_BASE}/api/auth/plan`, {
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(`Auth failed: ${response.status} ${response.statusText}`);
}
return response.json();
}
async function main() {
const apiKey = "sk_live_4f8a2b1c9d3e7f6a0b5c8d2e1f4a7b3c";
try {
const plan = await checkPlan(apiKey);
console.log(`Plan: ${plan.plan}`);
console.log(`Generations: ${plan.generationsUsed} / ${plan.generationsLimit}`);
console.log(`Multi-language support: ${plan.features.multiLanguage}`);
if (plan.generationsUsed >= plan.generationsLimit) {
console.log("Generation limit reached — upgrade your plan to continue.");
return;
}
console.log("Key is valid. Ready to generate docs.");
// Expected output:
// Plan: pro
// Generations: 42 / 500
// Multi-language support: true
// Key is valid. Ready to generate docs.
} catch (err) {
console.error("Could not verify API key:", (err as Error).message);
}
}
main();
requirePro
async function requirePro(commandName: string): Promise<boolean>
Use requirePro to gate CLI commands behind a Pro subscription, blocking execution and prompting an upgrade if the user isn't on a paid plan.
Call this at the top of any command handler that requires a Pro license — generate --multi-lang, generate --by-topic, or any other feature you want to restrict to paying users. It handles the license check, user messaging, and early exit for you.
When called, it reads the stored license from the system keychain, validates it against the skrypt API, and returns true only if the license is active and on a Pro plan. If validation fails, it prints an upgrade prompt to stderr and returns false — your command should exit immediately in that case.
| Name | Type | Required | Description |
|---|---|---|---|
commandName | string | Yes | The CLI command being protected (e.g. "generate --multi-lang"). Shown in the upgrade prompt so users know exactly which feature requires Pro. |
Returns true if the user has a valid Pro license — proceed with command execution. Returns false if they're on the free plan or unlicensed — exit the command handler without running the feature. Set SKRYPT_DEV=1 in your environment to bypass the check during internal development.
Heads up:
- Always check the return value and return early —
requireProwon't throw or callprocess.exit()itself, so ignoring afalsereturn will silently run the gated feature anyway.
Example:
// Simulated keychain and license state
const mockLicense = {
key: 'sk_pro_4f8a2b1c9d3e7f6a',
plan: 'pro',
email: 'ada@example.com',
}
// Inline mock of requirePro — mirrors real behavior without importing autodocs
async function validateLicense(licenseKey: string): Promise<{ valid: boolean; plan: string; email: string }> {
// Simulate API validation
if (licenseKey.startsWith('sk_pro_')) {
return { valid: true, plan: 'pro', email: mockLicense.email }
}
return { valid: false, plan: 'free', email: '', error: 'Invalid license key' } as any
}
async function requirePro(commandName: string): Promise<boolean> {
if (process.env.SKRYPT_DEV === '1') {
return true
}
const storedKey = mockLicense.key // in production: retrieved from keychain
if (!storedKey) {
console.error(`✗ "${commandName}" requires a Pro license.`)
console.error(` Upgrade at https://skrypt.ai/pricing`)
return false
}
const result = await validateLicense(storedKey)
if (!result.valid || result.plan !== 'pro') {
console.error(`✗ "${commandName}" requires a Pro license. You're on the ${result.plan} plan.`)
console.error(` Upgrade at https://skrypt.ai/pricing`)
return false
}
return true
}
// Simulated "generate --multi-lang" command handler
async function generateMultiLang(source: string, output: string) {
try {
const allowed = await requirePro('generate --multi-lang')
if (!allowed) {
process.exitCode = 1
return
}
// Pro-only logic runs here
console.log(`✓ License verified for ada@example.com`)
console.log(`Generating multi-language docs from ${source} → ${output}`)
// => Generating multi-language docs from ./src → ./docs
} catch (err) {
console.error('Unexpected error:', err)
process.exitCode = 1
}
}
generateMultiLang('./src', './docs')