@letterapp/cli 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/output.ts","../src/config.ts","../src/client.ts","../src/browser.ts","../src/env-file.ts","../src/pm.ts","../src/commands/login.ts","../src/commands/auth.ts","../src/commands/status.ts","../src/commands/config.ts","../src/commands/resources.ts"],"sourcesContent":["declare const PKG_VERSION: string;\nimport { Command } from \"commander\";\nimport { setJsonMode } from \"./output.js\";\nimport { registerLoginCommand } from \"./commands/login.js\";\nimport { registerAuthCommands } from \"./commands/auth.js\";\nimport { registerStatusCommand } from \"./commands/status.js\";\nimport { registerConfigCommands } from \"./commands/config.js\";\nimport { registerResourceCommands } from \"./commands/resources.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"letter\")\n .description(\"Connect your app to Letter, then manage it from the command line\")\n .version(PKG_VERSION)\n .option(\"--json\", \"Output raw JSON (for scripting / agents)\")\n .hook(\"preAction\", (thisCommand) => {\n if (thisCommand.opts().json) setJsonMode(true);\n });\n\n// Two layers, one login:\n// - login/auth/status/config wire up the project and its ingestion key\n// (lt_live_*, written to the project env as LETTER_API_KEY).\n// - the resource groups drive the Management API with the workspace PAT\n// (lt_pat_*, stored in ~/.letter/credentials.json). Both credentials are\n// minted by `letter login`; each command picks the right one on its own.\nregisterLoginCommand(program); // default command (`letter` == `letter login`)\nregisterAuthCommands(program);\nregisterStatusCommand(program);\nregisterConfigCommands(program);\nregisterResourceCommands(program); // control-plane resource groups (PAT-backed)\n\nprogram.parseAsync();\n","import chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\nimport { createInterface } from \"node:readline\";\n\nlet jsonMode = false;\n\nexport function setJsonMode(enabled: boolean) {\n jsonMode = enabled;\n}\nexport function isJsonMode() {\n return jsonMode;\n}\n\nexport function printJson(data: unknown) {\n console.log(JSON.stringify(data, null, 2));\n}\n\nexport function log(msg = \"\") {\n if (jsonMode) return;\n console.log(msg);\n}\n\nexport function printSuccess(msg: string) {\n if (jsonMode) return;\n console.log(chalk.green(\"✓\") + \" \" + msg);\n}\n\nexport function printInfo(msg: string) {\n if (jsonMode) return;\n console.log(chalk.cyan(\"›\") + \" \" + msg);\n}\n\nexport function printWarning(msg: string) {\n if (jsonMode) return;\n console.log(chalk.yellow(\"!\") + \" \" + msg);\n}\n\nexport function printError(err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n if (jsonMode) {\n printJson({ error: msg });\n return;\n }\n console.error(chalk.red(\"✗\") + \" \" + msg);\n}\n\n/** The Letter wordmark banner. Suppressed in JSON mode. */\nexport function banner() {\n if (jsonMode) return;\n console.log(\"\");\n console.log(\" \" + chalk.bold(\"Letter\") + chalk.red(\".\") + \" \" + chalk.dim(\"CLI\"));\n console.log(\"\");\n}\n\nexport function spinner(text: string): Ora {\n return ora({ text, color: \"cyan\", isEnabled: !jsonMode });\n}\n\n/**\n * Prompts the user and resolves with their input. Returns \"\" immediately on a\n * non-interactive stdin so automated/agent runs don't hang.\n */\nexport function prompt(question: string): Promise<string> {\n if (!process.stdin.isTTY) return Promise.resolve(\"\");\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Print a management API result. In `--json` mode, emits the raw payload so\n * agents/scripts can parse it. In human mode, renders list envelopes as a\n * compact `id label` table and single resources as pretty JSON.\n */\nexport function emit(data: unknown) {\n if (jsonMode) {\n printJson(data);\n return;\n }\n if (data && typeof data === \"object\" && Array.isArray((data as { data?: unknown }).data)) {\n const env = data as { data: Record<string, unknown>[]; next_cursor?: string | null };\n if (env.data.length === 0) {\n console.log(chalk.dim(\"(none)\"));\n } else {\n for (const row of env.data) {\n const id = String(row.id ?? row.slug ?? row.external_id ?? \"\");\n const label =\n (row.name as string) ??\n (row.email as string) ??\n (row.domain as string) ??\n (row.subject as string) ??\n \"\";\n console.log(`${chalk.bold(id)}${label ? \" \" + label : \"\"}`);\n }\n }\n if (env.next_cursor) {\n console.log(chalk.dim(`\\n› more: --cursor ${env.next_cursor}`));\n }\n return;\n }\n printJson(data);\n}\n\nexport const c = chalk;\n","import Conf from \"conf\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { mkdir, readFile, writeFile, rm } from \"node:fs/promises\";\n\n/** The SDK / API default. When the resolved base equals this we don't bother\n * writing LETTER_BASE_URL into the project env. */\nexport const DEFAULT_BASE_URL = \"https://api.letter.app\";\n\n/* -------------------------------------------------------------------------- */\n/* Non-secret CLI preferences (base URL). */\n/* Stored with `conf`, mirroring how the SDK/MCP resolve their endpoint. */\n/* -------------------------------------------------------------------------- */\n\nconst config = new Conf({\n projectName: \"letterapp-cli\",\n schema: {\n baseUrl: { type: \"string\", default: DEFAULT_BASE_URL },\n },\n});\n\n/** Resolve the API base: explicit flag > env > stored config > prod default. */\nexport function getBaseUrl(flag?: string): string {\n const raw =\n flag ||\n process.env.LETTER_BASE_URL ||\n (config.get(\"baseUrl\") as string) ||\n DEFAULT_BASE_URL;\n return raw.replace(/\\/$/, \"\");\n}\n\nexport function setBaseUrl(url: string): void {\n config.set(\"baseUrl\", url.replace(/\\/$/, \"\"));\n}\n\nexport function getConfigPath(): string {\n return config.path;\n}\n\nexport function resetConfig(): void {\n config.clear();\n}\n\n/* -------------------------------------------------------------------------- */\n/* Secret credential store (~/.letter/credentials.json). */\n/* Written here, read by @letterapp/mcp so the secret never lands in an MCP */\n/* config. Kept separate from `conf` and locked to owner-only permissions. */\n/* -------------------------------------------------------------------------- */\n\nexport type StoredCredential = {\n apiKey: string;\n /** Workspace Personal Access Token (lt_pat_*) for the management API/CLI. */\n pat?: string;\n baseUrl: string;\n project: { slug: string; name: string };\n workspace?: { id: string; name: string };\n savedAt: string;\n};\n\n/**\n * Resolve the project slug a management command should target: explicit\n * `--project` flag > `LETTER_PROJECT` env (for scripts/CI) > the connected\n * project from `letter login`. Throws a clear error when none is available so\n * scripts fail loudly.\n */\nexport async function resolveProjectSlug(flag?: string): Promise<string> {\n if (flag) return flag;\n if (process.env.LETTER_PROJECT) return process.env.LETTER_PROJECT;\n const cred = await readCredential();\n if (cred?.project?.slug) return cred.project.slug;\n throw new Error(\n \"No project. Pass --project <slug>, set LETTER_PROJECT, or run `letter login`.\",\n );\n}\n\nexport function credentialsPath(): string {\n return path.join(homedir(), \".letter\", \"credentials.json\");\n}\n\nexport async function saveCredential(cred: StoredCredential): Promise<string> {\n const file = credentialsPath();\n await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });\n await writeFile(file, `${JSON.stringify(cred, null, 2)}\\n`, { mode: 0o600 });\n return file;\n}\n\nexport async function readCredential(): Promise<StoredCredential | null> {\n try {\n const raw = await readFile(credentialsPath(), \"utf8\");\n return JSON.parse(raw) as StoredCredential;\n } catch {\n return null;\n }\n}\n\nexport async function clearCredential(): Promise<void> {\n await rm(credentialsPath(), { force: true });\n}\n","import { getBaseUrl, readCredential } from \"./config.js\";\n\nconst USER_AGENT = \"@letterapp/cli\";\n\n/* -------------------------------------------------------------------------- */\n/* Device authorization (RFC 8628 style). No secret ever passes through argv. */\n/* -------------------------------------------------------------------------- */\n\nexport type StartResponse = {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n interval: number;\n expires_in: number;\n};\n\nexport type PollResponse =\n | { status: \"authorization_pending\" }\n | { status: \"slow_down\"; retryAfter?: number }\n | { status: \"access_denied\" }\n | { status: \"expired_token\" }\n | {\n status: \"approved\";\n api_key: string;\n /** Workspace PAT for the management API (lt_pat_*). */\n pat: string;\n base_url: string;\n project: { slug: string; name: string };\n workspace?: { id: string; name: string };\n };\n\n/** Starts a device-authorization flow against the given API base. */\nexport async function startDeviceAuth(base: string): Promise<StartResponse> {\n const res = await fetch(`${base}/v1/cli/auth/start`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"user-agent\": USER_AGENT },\n body: \"{}\",\n });\n if (!res.ok) {\n throw new Error(\n `Could not start login (HTTP ${res.status}). Is ${base} reachable?`,\n );\n }\n return (await res.json()) as StartResponse;\n}\n\n/** Polls once for approval. */\nexport async function pollDeviceAuth(\n base: string,\n deviceCode: string,\n): Promise<PollResponse> {\n const res = await fetch(`${base}/v1/cli/auth/poll`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"user-agent\": USER_AGENT },\n body: JSON.stringify({ device_code: deviceCode }),\n });\n\n if (res.status === 429) {\n const retryAfter = Number(res.headers.get(\"retry-after\") ?? \"5\");\n return { status: \"slow_down\", retryAfter };\n }\n if (!res.ok) {\n throw new Error(`Login poll failed (HTTP ${res.status}).`);\n }\n return (await res.json()) as PollResponse;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Authenticated client. Resolves the key from the stored credential (written */\n/* by `letter login`) or LETTER_API_KEY. Used by data commands like `status`. */\n/* -------------------------------------------------------------------------- */\n\nexport interface ApiResponse<T = unknown> {\n status: number;\n data: T;\n}\n\nclass LetterClient {\n private maxRetries = 3;\n\n private async resolveAuth(): Promise<{ base: string; token: string }> {\n const cred = await readCredential();\n const token = process.env.LETTER_API_KEY || cred?.apiKey || \"\";\n if (!token) {\n throw new Error(\n \"Not connected. Run `letter login` (or set LETTER_API_KEY) first.\",\n );\n }\n const base = getBaseUrl(process.env.LETTER_API_KEY ? undefined : cred?.baseUrl);\n return { base, token };\n }\n\n private async request<T = unknown>(\n method: string,\n path: string,\n attempt = 1,\n ): Promise<ApiResponse<T>> {\n const { base, token } = await this.resolveAuth();\n const res = await fetch(`${base}${path}`, {\n method,\n headers: {\n authorization: `Bearer ${token}`,\n accept: \"application/json\",\n \"user-agent\": USER_AGENT,\n },\n });\n\n if (res.status === 429 && attempt <= this.maxRetries) {\n const retryAfter = Number(res.headers.get(\"retry-after\") || 2);\n await new Promise((r) => setTimeout(r, retryAfter * 1000 * attempt));\n return this.request<T>(method, path, attempt + 1);\n }\n\n const data = (res.status === 204 ? {} : await res.json()) as T;\n if (!res.ok) {\n const msg =\n (data as { error?: { message?: string } })?.error?.message ||\n `HTTP ${res.status}`;\n const err = new Error(msg) as Error & { status: number };\n err.status = res.status;\n throw err;\n }\n return { status: res.status, data };\n }\n\n get<T = unknown>(path: string) {\n return this.request<T>(\"GET\", path);\n }\n}\n\nexport const client = new LetterClient();\n\n/* -------------------------------------------------------------------------- */\n/* Management client. Authenticates with the workspace PAT (lt_pat_*) and */\n/* drives the control-plane (/v1/projects, sequences, broadcasts, ...). Used by */\n/* the resource command groups. */\n/* -------------------------------------------------------------------------- */\n\nexport type RequestInitLite = {\n body?: unknown;\n query?: Record<string, string | number | undefined>;\n form?: FormData;\n};\n\nclass ManagementClient {\n private maxRetries = 3;\n\n private async resolveAuth(): Promise<{ base: string; token: string }> {\n const cred = await readCredential();\n const token = process.env.LETTER_PAT || cred?.pat || \"\";\n if (!token) {\n throw new Error(\n \"Not connected for management. Run `letter login` (or set LETTER_PAT) first.\",\n );\n }\n const base = getBaseUrl(process.env.LETTER_PAT ? undefined : cred?.baseUrl);\n return { base, token };\n }\n\n async request<T = unknown>(\n method: string,\n path: string,\n init: RequestInitLite = {},\n attempt = 1,\n ): Promise<ApiResponse<T>> {\n const { base, token } = await this.resolveAuth();\n\n let url = `${base}${path}`;\n if (init.query) {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(init.query)) {\n if (v !== undefined && v !== \"\") qs.set(k, String(v));\n }\n const s = qs.toString();\n if (s) url += `?${s}`;\n }\n\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n accept: \"application/json\",\n \"user-agent\": USER_AGENT,\n };\n let body: BodyInit | undefined;\n if (init.form) {\n body = init.form;\n } else if (init.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n body = JSON.stringify(init.body);\n }\n\n const res = await fetch(url, { method, headers, body });\n\n if (res.status === 429 && attempt <= this.maxRetries) {\n const retryAfter = Number(res.headers.get(\"retry-after\") || 2);\n await new Promise((r) => setTimeout(r, retryAfter * 1000 * attempt));\n return this.request<T>(method, path, init, attempt + 1);\n }\n\n const text = await res.text();\n const data = (text ? JSON.parse(text) : {}) as T;\n if (!res.ok) {\n const msg =\n (data as { error?: { message?: string } })?.error?.message ||\n `HTTP ${res.status}`;\n const err = new Error(msg) as Error & { status: number };\n err.status = res.status;\n throw err;\n }\n return { status: res.status, data };\n }\n\n get<T = unknown>(path: string, query?: RequestInitLite[\"query\"]) {\n return this.request<T>(\"GET\", path, { query });\n }\n post<T = unknown>(path: string, body?: unknown) {\n return this.request<T>(\"POST\", path, { body });\n }\n postForm<T = unknown>(path: string, form: FormData) {\n return this.request<T>(\"POST\", path, { form });\n }\n patch<T = unknown>(path: string, body?: unknown) {\n return this.request<T>(\"PATCH\", path, { body });\n }\n put<T = unknown>(path: string, body?: unknown) {\n return this.request<T>(\"PUT\", path, { body });\n }\n delete<T = unknown>(path: string) {\n return this.request<T>(\"DELETE\", path);\n }\n}\n\nexport const mgmt = new ManagementClient();\n","import { spawn } from \"node:child_process\";\n\n/**\n * Opens `url` in the default browser. Best-effort and non-blocking: if it fails\n * (headless box, no DISPLAY) the caller has already printed the URL so the user\n * can open it manually. Returns true if a launcher was spawned.\n */\nexport function openUrl(url: string): boolean {\n const platform = process.platform;\n let command: string;\n let args: string[];\n\n if (platform === \"darwin\") {\n command = \"open\";\n args = [url];\n } else if (platform === \"win32\") {\n command = \"cmd\";\n // `start` needs an empty title arg; the comma-free form avoids quoting woes.\n args = [\"/c\", \"start\", \"\", url];\n } else {\n command = \"xdg-open\";\n args = [url];\n }\n\n try {\n const child = spawn(command, args, { stdio: \"ignore\", detached: true });\n child.on(\"error\", () => {});\n child.unref();\n return true;\n } catch {\n return false;\n }\n}\n","import path from \"node:path\";\nimport { readFile, writeFile, stat } from \"node:fs/promises\";\n\n/**\n * Upserts `key=value` in an env file, creating it if needed. Existing keys are\n * replaced in place; new keys are appended. Returns the file path. The value is\n * never logged by this module - callers print only the key name.\n */\nexport async function upsertEnv(\n cwd: string,\n file: string,\n entries: Record<string, string>,\n): Promise<string> {\n const filePath = path.join(cwd, file);\n let contents = \"\";\n try {\n contents = await readFile(filePath, \"utf8\");\n } catch {\n contents = \"\";\n }\n\n let next = contents;\n for (const [key, value] of Object.entries(entries)) {\n const line = `${key}=${value}`;\n const re = new RegExp(`^${escapeRegExp(key)}=.*$`, \"m\");\n if (re.test(next)) {\n next = next.replace(re, line);\n } else {\n if (next.length && !next.endsWith(\"\\n\")) next += \"\\n\";\n next += `${line}\\n`;\n }\n }\n\n await writeFile(filePath, next, \"utf8\");\n return filePath;\n}\n\n/** True if a file exists at `cwd/name`. */\nexport async function fileExists(cwd: string, name: string): Promise<boolean> {\n try {\n await stat(path.join(cwd, name));\n return true;\n } catch {\n return false;\n }\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { spawn } from \"node:child_process\";\nimport { readFile, readdir } from \"node:fs/promises\";\nimport { fileExists } from \"./env-file.js\";\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\n/**\n * Detects the package manager from lockfiles (then the `npm_config_user_agent`\n * of the running process), defaulting to npm.\n */\nexport async function detectPackageManager(\n cwd: string,\n): Promise<PackageManager> {\n if (await fileExists(cwd, \"pnpm-lock.yaml\")) return \"pnpm\";\n if (await fileExists(cwd, \"yarn.lock\")) return \"yarn\";\n if (await fileExists(cwd, \"bun.lockb\")) return \"bun\";\n if (await fileExists(cwd, \"package-lock.json\")) return \"npm\";\n\n const ua = process.env.npm_config_user_agent ?? \"\";\n if (ua.startsWith(\"pnpm\")) return \"pnpm\";\n if (ua.startsWith(\"yarn\")) return \"yarn\";\n if (ua.startsWith(\"bun\")) return \"bun\";\n return \"npm\";\n}\n\n/** Detects a likely Node web framework for friendlier guidance. */\nexport async function detectFramework(cwd: string): Promise<string | null> {\n try {\n const pkg = JSON.parse(await readFile(`${cwd}/package.json`, \"utf8\")) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (deps.next) return \"Next.js\";\n if (deps.nuxt) return \"Nuxt\";\n if (deps[\"@remix-run/node\"] || deps[\"@remix-run/react\"]) return \"Remix\";\n if (deps.express) return \"Express\";\n if (deps.fastify) return \"Fastify\";\n if (deps.hono) return \"Hono\";\n if (deps[\"@sveltejs/kit\"]) return \"SvelteKit\";\n return null;\n } catch {\n return null;\n }\n}\n\nexport type Runtime = \"node\" | \"python\" | \"ruby\" | \"go\" | \"php\" | \"other\";\nexport type Stack = { runtime: Runtime; framework: string | null };\n\n/**\n * Detects the project's language/runtime so the CLI installs the right thing\n * (only Node has an SDK today) and prints instructions in the right language.\n * Node is checked first since it's this CLI's primary audience.\n */\nexport async function detectStack(cwd: string): Promise<Stack> {\n if (await fileExists(cwd, \"package.json\")) {\n return { runtime: \"node\", framework: await detectFramework(cwd) };\n }\n if (\n (await fileExists(cwd, \"pyproject.toml\")) ||\n (await fileExists(cwd, \"requirements.txt\")) ||\n (await fileExists(cwd, \"Pipfile\")) ||\n (await fileExists(cwd, \"setup.py\"))\n ) {\n return { runtime: \"python\", framework: await detectPythonFramework(cwd) };\n }\n if ((await fileExists(cwd, \"Gemfile\")) || (await hasGemspec(cwd))) {\n return { runtime: \"ruby\", framework: await detectRubyFramework(cwd) };\n }\n if (await fileExists(cwd, \"go.mod\")) return { runtime: \"go\", framework: null };\n if (await fileExists(cwd, \"composer.json\")) {\n return { runtime: \"php\", framework: await detectPhpFramework(cwd) };\n }\n return { runtime: \"other\", framework: null };\n}\n\n/** Human-friendly label for the detected stack (framework if known). */\nexport function stackLabel(stack: Stack): string {\n if (stack.framework) return stack.framework;\n switch (stack.runtime) {\n case \"node\":\n return \"a Node.js project\";\n case \"python\":\n return \"a Python project\";\n case \"ruby\":\n return \"a Ruby project\";\n case \"go\":\n return \"a Go project\";\n case \"php\":\n return \"a PHP project\";\n default:\n return \"your project\";\n }\n}\n\n/** The language name to tell the agent to write in. */\nexport function languageName(stack: Stack): string {\n switch (stack.runtime) {\n case \"node\":\n return \"TypeScript\";\n case \"python\":\n return \"Python\";\n case \"ruby\":\n return \"Ruby\";\n case \"go\":\n return \"Go\";\n case \"php\":\n return \"PHP\";\n default:\n return \"your language\";\n }\n}\n\nasync function readIf(cwd: string, name: string): Promise<string> {\n try {\n return await readFile(`${cwd}/${name}`, \"utf8\");\n } catch {\n return \"\";\n }\n}\n\nasync function hasGemspec(cwd: string): Promise<boolean> {\n try {\n return (await readdir(cwd)).some((f) => f.endsWith(\".gemspec\"));\n } catch {\n return false;\n }\n}\n\nasync function detectPythonFramework(cwd: string): Promise<string | null> {\n const txt = (\n (await readIf(cwd, \"requirements.txt\")) +\n (await readIf(cwd, \"pyproject.toml\")) +\n (await readIf(cwd, \"Pipfile\"))\n ).toLowerCase();\n if (txt.includes(\"django\")) return \"Django\";\n if (txt.includes(\"fastapi\")) return \"FastAPI\";\n if (txt.includes(\"flask\")) return \"Flask\";\n return null;\n}\n\nasync function detectRubyFramework(cwd: string): Promise<string | null> {\n const txt = (await readIf(cwd, \"Gemfile\")).toLowerCase();\n if (txt.includes(\"rails\")) return \"Rails\";\n if (txt.includes(\"sinatra\")) return \"Sinatra\";\n return null;\n}\n\nasync function detectPhpFramework(cwd: string): Promise<string | null> {\n const txt = (await readIf(cwd, \"composer.json\")).toLowerCase();\n if (txt.includes(\"laravel\")) return \"Laravel\";\n if (txt.includes(\"symfony\")) return \"Symfony\";\n return null;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Non-Node SDK installs (letterapp on PyPI / RubyGems) */\n/* -------------------------------------------------------------------------- */\n\nconst SDK_PYPI = \"letterapp\";\nconst SDK_GEM = \"letterapp\";\n\nexport type SdkInstall = { cmd: string; args: string[]; display: string };\n\nfunction sdkInstall(cmd: string, args: string[]): SdkInstall {\n return { cmd, args, display: `${cmd} ${args.join(\" \")}` };\n}\n\n/**\n * Picks the right Python installer for the project: uv > poetry > pipenv > pip.\n * Returns the command we'd run to add `letterapp`.\n */\nexport async function pythonSdkInstall(cwd: string): Promise<SdkInstall> {\n if (await fileExists(cwd, \"uv.lock\")) return sdkInstall(\"uv\", [\"add\", SDK_PYPI]);\n const pyproject = (await readIf(cwd, \"pyproject.toml\")).toLowerCase();\n if (\n (await fileExists(cwd, \"poetry.lock\")) ||\n pyproject.includes(\"[tool.poetry]\")\n ) {\n return sdkInstall(\"poetry\", [\"add\", SDK_PYPI]);\n }\n if (await fileExists(cwd, \"Pipfile\")) {\n return sdkInstall(\"pipenv\", [\"install\", SDK_PYPI]);\n }\n return sdkInstall(\"pip\", [\"install\", SDK_PYPI]);\n}\n\n/** Bundler when there's a Gemfile, otherwise a plain `gem install`. */\nexport async function rubySdkInstall(cwd: string): Promise<SdkInstall> {\n if (await fileExists(cwd, \"Gemfile\")) {\n return sdkInstall(\"bundle\", [\"add\", SDK_GEM]);\n }\n return sdkInstall(\"gem\", [\"install\", SDK_GEM]);\n}\n\n/** Runs an SdkInstall command, streaming output. Resolves to the exit code. */\nexport function runSdkInstall(\n install: SdkInstall,\n cwd: string,\n): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn(install.cmd, install.args, {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n child.on(\"error\", () => resolve(1));\n child.on(\"close\", (code) => resolve(code ?? 1));\n });\n}\n\nexport function installCommand(pm: PackageManager, pkg: string): string {\n switch (pm) {\n case \"pnpm\":\n return `pnpm add ${pkg}`;\n case \"yarn\":\n return `yarn add ${pkg}`;\n case \"bun\":\n return `bun add ${pkg}`;\n default:\n return `npm install ${pkg}`;\n }\n}\n\n/** Runs the install command, streaming output. Resolves to the exit code. */\nexport function runInstall(\n pm: PackageManager,\n pkg: string,\n cwd: string,\n): Promise<number> {\n const args = pm === \"npm\" ? [\"install\", pkg] : [\"add\", pkg];\n return new Promise((resolve) => {\n const child = spawn(pm, args, {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n child.on(\"error\", () => resolve(1));\n child.on(\"close\", (code) => resolve(code ?? 1));\n });\n}\n","import { Command } from \"commander\";\nimport {\n getBaseUrl,\n DEFAULT_BASE_URL,\n saveCredential,\n} from \"../config.js\";\nimport { startDeviceAuth, pollDeviceAuth } from \"../client.js\";\nimport { openUrl } from \"../browser.js\";\nimport { upsertEnv } from \"../env-file.js\";\nimport {\n detectPackageManager,\n detectStack,\n installCommand,\n languageName,\n pythonSdkInstall,\n rubySdkInstall,\n runInstall,\n runSdkInstall,\n stackLabel,\n type SdkInstall,\n type Stack,\n} from \"../pm.js\";\nimport {\n banner,\n c,\n log,\n printError,\n printInfo,\n printSuccess,\n printWarning,\n prompt,\n} from \"../output.js\";\n\nconst SDK_PACKAGE = \"@letterapp/node\";\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n// Node projects use .env.local (Next.js convention, gitignored by default);\n// everything else uses the more universal .env.\nfunction envFileFor(stack: Stack): string {\n return stack.runtime === \"node\" ? \".env.local\" : \".env\";\n}\n\ninterface LoginOptions {\n open: boolean;\n install: boolean;\n yes?: boolean;\n baseUrl?: string;\n apiKey?: string;\n}\n\nexport function registerLoginCommand(program: Command) {\n program\n .command(\"login\", { isDefault: true })\n .alias(\"init\")\n .description(\"Connect this project to Letter (interactive device login)\")\n .option(\"--no-open\", \"Don't open the browser; print the URL to open manually\")\n .option(\"-y, --yes\", \"Non-interactive: don't wait for Enter (agents/CI)\")\n .option(\"--no-install\", \"Skip installing the SDK\")\n .option(\"--base-url <url>\", \"Target a self-hosted or local Letter instance\")\n .option(\n \"--api-key <key>\",\n \"CI only: write a key without the device flow (never use in chat)\",\n )\n .action(async (opts: LoginOptions) => {\n const code = await runLogin(opts);\n if (code !== 0) process.exitCode = code;\n });\n}\n\nasync function runLogin(opts: LoginOptions): Promise<number> {\n const base = getBaseUrl(opts.baseUrl);\n const cwd = process.cwd();\n const interactive = process.stdin.isTTY && !opts.yes;\n\n banner();\n\n // CI escape hatch: write the provided key non-interactively. Documented as\n // automation-only; interactive/agent use should rely on the device flow so\n // no secret passes through the command line or chat.\n if (opts.apiKey) {\n const envFile = envFileFor(await detectStack(cwd));\n const entries: Record<string, string> = { LETTER_API_KEY: opts.apiKey };\n if (base !== DEFAULT_BASE_URL) entries.LETTER_BASE_URL = base;\n const file = await upsertEnv(cwd, envFile, entries);\n printSuccess(`Saved LETTER_API_KEY to ${rel(cwd, file)} (--api-key).`);\n printWarning(\"--api-key is for CI. For interactive setup, run `letter login`.\");\n return 0;\n }\n\n // 1. Begin the flow.\n let flow;\n try {\n flow = await startDeviceAuth(base);\n } catch (err) {\n printError(err);\n return 1;\n }\n\n log(c.bold(\"Confirm this code in your browser:\"));\n log();\n log(\" \" + c.cyan(c.bold(flow.user_code)));\n log();\n\n // 2. Open the browser (interactive: wait for Enter; otherwise auto/print).\n if (interactive && opts.open) {\n await prompt(c.dim(\"Press Enter to open your browser… \"));\n }\n if (opts.open) openUrl(flow.verification_uri_complete);\n printInfo(\"If your browser didn't open, visit:\");\n log(\" \" + c.blue(flow.verification_uri_complete));\n log();\n printInfo(\"Waiting for you to approve… (Ctrl+C to cancel)\");\n\n // 3. Poll until the user approves/denies or the code expires.\n const deadline = Date.now() + flow.expires_in * 1000;\n let intervalMs = Math.max(1, flow.interval) * 1000;\n\n while (Date.now() < deadline) {\n await sleep(intervalMs);\n let res;\n try {\n res = await pollDeviceAuth(base, flow.device_code);\n } catch (err) {\n printError(err);\n return 1;\n }\n\n if (res.status === \"authorization_pending\") continue;\n if (res.status === \"slow_down\") {\n intervalMs += 1000;\n continue;\n }\n if (res.status === \"access_denied\") {\n printError(new Error(\"Request denied in the browser. Nothing was changed.\"));\n return 1;\n }\n if (res.status === \"expired_token\") {\n printError(new Error(\"This login expired. Run the command again to retry.\"));\n return 1;\n }\n\n return finish(res, cwd, opts.install);\n }\n\n printError(new Error(\"Timed out waiting for approval. Run the command again.\"));\n return 1;\n}\n\nasync function finish(\n approved: {\n api_key: string;\n pat: string;\n base_url: string;\n project: { slug: string; name: string };\n workspace?: { id: string; name: string };\n },\n cwd: string,\n doInstall: boolean,\n): Promise<number> {\n const { api_key: apiKey, pat, base_url: baseUrl, project, workspace } = approved;\n log();\n printSuccess(`Approved for project ${c.bold(project.name)}.`);\n\n const stack = await detectStack(cwd);\n const isNode = stack.runtime === \"node\";\n const envFile = envFileFor(stack);\n\n // Write the key to the project env (value never printed) + shared store.\n const entries: Record<string, string> = { LETTER_API_KEY: apiKey };\n if (baseUrl && baseUrl !== DEFAULT_BASE_URL) entries.LETTER_BASE_URL = baseUrl;\n const written = await upsertEnv(cwd, envFile, entries);\n printSuccess(`Saved LETTER_API_KEY to ${rel(cwd, written)}.`);\n\n try {\n const credFile = await saveCredential({\n apiKey,\n pat,\n baseUrl: baseUrl || DEFAULT_BASE_URL,\n project,\n workspace,\n savedAt: new Date().toISOString(),\n });\n printSuccess(`Stored credentials in ${tildify(credFile)} for tooling (MCP + CLI).`);\n } catch {\n printWarning(\"Could not write ~/.letter/credentials.json (continuing).\");\n }\n\n // Install the SDK for the detected language. Node, Python, and Ruby have\n // official SDKs; everything else integrates over the HTTP API, so we skip the\n // install and print HTTP instructions instead.\n if (isNode) {\n const pm = await detectPackageManager(cwd);\n if (doInstall) {\n printInfo(`Installing ${SDK_PACKAGE} with ${pm}…`);\n const code = await runInstall(pm, SDK_PACKAGE, cwd);\n if (code === 0) printSuccess(`Installed ${SDK_PACKAGE}.`);\n else printWarning(`Install failed. Run: ${installCommand(pm, SDK_PACKAGE)}`);\n } else {\n printInfo(`Skipped install. Run: ${installCommand(pm, SDK_PACKAGE)}`);\n }\n printNodeInstructions(stack.framework, envFile);\n } else if (stack.runtime === \"python\" || stack.runtime === \"ruby\") {\n const install =\n stack.runtime === \"python\"\n ? await pythonSdkInstall(cwd)\n : await rubySdkInstall(cwd);\n await installSdk(install, doInstall);\n if (stack.runtime === \"python\") {\n printPythonInstructions(stack.framework, envFile);\n } else {\n printRubyInstructions(stack.framework, envFile);\n }\n } else {\n printInfo(\n `Detected ${stackLabel(stack)}. No ${languageName(stack)} SDK yet, so use the HTTP API - no package installed.`,\n );\n printHttpInstructions(stack, envFile, baseUrl || DEFAULT_BASE_URL);\n }\n return 0;\n}\n\n/** Shared install-or-print step for the non-Node SDKs (letterapp). */\nasync function installSdk(install: SdkInstall, doInstall: boolean): Promise<void> {\n if (doInstall) {\n printInfo(`Installing letterapp (${install.display})…`);\n const code = await runSdkInstall(install, process.cwd());\n if (code === 0) printSuccess(\"Installed letterapp.\");\n else printWarning(`Install failed. Run: ${install.display}`);\n } else {\n printInfo(`Skipped install. Run: ${install.display}`);\n }\n}\n\n/**\n * Agent-ready instructions for the last mile. The CLI deliberately does NOT\n * scaffold or edit source itself: it hands these steps to the coding agent,\n * which has the repo context to place the calls correctly in the right\n * language. A human can follow them too.\n *\n * Node path: use the installed SDK.\n */\nfunction printNodeInstructions(framework: string | null, envFile: string): void {\n log();\n log(c.bold(\"Next: hand these steps to your coding agent\"));\n log(\n c.dim(\n framework\n ? `Detected ${framework}. The SDK is installed and LETTER_API_KEY is in ${envFile}.`\n : `The SDK is installed and LETTER_API_KEY is in ${envFile}.`,\n ),\n );\n log();\n log(\n c.bold(\" 1. \") +\n `Create a server-side client (e.g. ${c.cyan(\"lib/letter.ts\")}):`,\n );\n log(c.dim(' import { Letter } from \"@letterapp/node\";'));\n log(c.dim(\" export const letter = new Letter({\"));\n log(c.dim(\" apiKey: process.env.LETTER_API_KEY!,\"));\n log(\n c.dim(\n \" ...(process.env.LETTER_BASE_URL ? { baseUrl: process.env.LETTER_BASE_URL } : {}),\",\n ),\n );\n log(c.dim(\" });\"));\n log();\n log(\n c.bold(\" 2. \") +\n `Call ${c.cyan(\"identify()\")} where users sign up or log in:`,\n );\n log(\n c.dim(\n \" await letter.identify({ userId: user.id, email: user.email });\",\n ),\n );\n log();\n log(\n c.bold(\" 3. \") +\n `Call ${c.cyan(\"track()\")} on your 2-3 most important actions:`,\n );\n log(\n c.dim(' await letter.track({ userId: user.id, event: \"Signed Up\" });'),\n );\n log();\n log(\n c.dim(\n \" In serverless handlers or scripts, await letter.flush() before returning.\",\n ),\n );\n log();\n log(c.dim(\"Verify it landed: \") + c.bold(\"letter status\"));\n log(\n c.dim(\n `Your API key is in ${envFile} - keep it out of source control, and don't echo its value.`,\n ),\n );\n log();\n}\n\n/** Python path: use the installed letterapp SDK. */\nfunction printPythonInstructions(framework: string | null, envFile: string): void {\n log();\n log(c.bold(\"Next: hand these steps to your coding agent\"));\n log(\n c.dim(\n framework\n ? `Detected ${framework}. The SDK is installed and LETTER_API_KEY is in ${envFile}.`\n : `The SDK is installed and LETTER_API_KEY is in ${envFile}.`,\n ),\n );\n log();\n log(\n c.bold(\" 1. \") +\n `Create a shared client (e.g. ${c.cyan(\"letter.py\")}):`,\n );\n log(c.dim(\" import os\"));\n log(c.dim(\" from letterapp import Letter\"));\n log(c.dim(' letter = Letter(api_key=os.environ[\"LETTER_API_KEY\"])'));\n log(c.dim(' # Self-hosted/local: pass base_url=os.environ[\"LETTER_BASE_URL\"].'));\n log();\n log(\n c.bold(\" 2. \") +\n `Call ${c.cyan(\"identify()\")} where users sign up or log in:`,\n );\n log(c.dim(\" letter.identify(user_id=user.id, email=user.email)\"));\n log();\n log(\n c.bold(\" 3. \") +\n `Call ${c.cyan(\"track()\")} on your 2-3 most important actions:`,\n );\n log(c.dim(' letter.track(user_id=user.id, event=\"Signed Up\")'));\n log();\n log(\n c.dim(\n \" In serverless handlers or scripts, call letter.flush() (or letter.close()) before returning.\",\n ),\n );\n log();\n log(c.dim(\"Verify it landed: \") + c.bold(\"letter status\"));\n log(\n c.dim(\n `Your API key is in ${envFile} - keep it out of source control, and don't echo its value.`,\n ),\n );\n log();\n}\n\n/** Ruby path: use the installed letterapp SDK. */\nfunction printRubyInstructions(framework: string | null, envFile: string): void {\n log();\n log(c.bold(\"Next: hand these steps to your coding agent\"));\n log(\n c.dim(\n framework\n ? `Detected ${framework}. The SDK is installed and LETTER_API_KEY is in ${envFile}.`\n : `The SDK is installed and LETTER_API_KEY is in ${envFile}.`,\n ),\n );\n log();\n log(\n c.bold(\" 1. \") +\n `Create a shared client (in Rails, ${c.cyan(\"config/initializers/letter.rb\")}):`,\n );\n log(c.dim(' require \"letterapp\"'));\n log(c.dim(' LETTER = Letterapp::Client.new(api_key: ENV[\"LETTER_API_KEY\"])'));\n log(c.dim(' # Self-hosted/local: pass base_url: ENV[\"LETTER_BASE_URL\"].'));\n log();\n log(\n c.bold(\" 2. \") +\n `Call ${c.cyan(\"identify\")} where users sign up or log in:`,\n );\n log(c.dim(\" LETTER.identify(user_id: user.id, email: user.email)\"));\n log();\n log(\n c.bold(\" 3. \") +\n `Call ${c.cyan(\"track\")} on your 2-3 most important actions:`,\n );\n log(c.dim(' LETTER.track(user_id: user.id, event: \"Signed Up\")'));\n log();\n log(\n c.dim(\n \" In serverless handlers or scripts, call LETTER.flush before returning.\",\n ),\n );\n log();\n log(c.dim(\"Verify it landed: \") + c.bold(\"letter status\"));\n log(\n c.dim(\n `Your API key is in ${envFile} - keep it out of source control, and don't echo its value.`,\n ),\n );\n log();\n}\n\n/**\n * Non-SDK path (Go, PHP, other): instruct the agent to call the ingestion HTTP\n * API directly in the project's own language.\n */\nfunction printHttpInstructions(\n stack: Stack,\n envFile: string,\n base: string,\n): void {\n log();\n log(c.bold(\"Next: hand these steps to your coding agent\"));\n log(\n c.dim(\n `Detected ${stackLabel(stack)}. There's no ${languageName(stack)} SDK yet, so call Letter's HTTP API directly in ${languageName(stack)} with your usual HTTP client.`,\n ),\n );\n log();\n log(\n c.dim(\n `LETTER_API_KEY is in ${envFile}. Read it from the environment; never inline it.`,\n ),\n );\n log();\n log(c.bold(\" 1. \") + \"identify users where they sign up or log in:\");\n log(c.dim(` POST ${base}/v1/identify`));\n log(c.dim(\" Authorization: Bearer $LETTER_API_KEY\"));\n log(c.dim(' { \"userId\": \"<your user id>\", \"email\": \"<email>\" }'));\n log();\n log(c.bold(\" 2. \") + \"track your 2-3 most important actions:\");\n log(c.dim(` POST ${base}/v1/track`));\n log(c.dim(\" Authorization: Bearer $LETTER_API_KEY\"));\n log(c.dim(' { \"userId\": \"<your user id>\", \"event\": \"Signed Up\" }'));\n log();\n log(\n c.dim(\n \" Send Content-Type: application/json. The same auth/shape covers group and batch.\",\n ),\n );\n log();\n log(c.dim(\"Verify it landed: \") + c.bold(\"letter status\"));\n log(\n c.dim(\n `Your API key is in ${envFile} - keep it out of source control, and don't echo its value.`,\n ),\n );\n log();\n}\n\nfunction rel(cwd: string, file: string): string {\n return file.startsWith(cwd) ? file.slice(cwd.length + 1) || file : file;\n}\n\nfunction tildify(file: string): string {\n const home = process.env.HOME || process.env.USERPROFILE;\n return home && file.startsWith(home) ? `~${file.slice(home.length)}` : file;\n}\n","import { Command } from \"commander\";\nimport { readCredential, clearCredential } from \"../config.js\";\nimport { c, isJsonMode, printJson, printInfo, printSuccess } from \"../output.js\";\n\nfunction mask(key: string): string {\n return key.length > 12 ? key.slice(0, 8) + \"…\" + key.slice(-4) : \"set\";\n}\n\nexport function registerAuthCommands(program: Command) {\n const auth = program.command(\"auth\").description(\"Manage the stored Letter connection\");\n\n auth\n .command(\"status\")\n .description(\"Show whether this machine is connected to Letter\")\n .action(async () => {\n const cred = await readCredential();\n const envKey = process.env.LETTER_API_KEY;\n\n if (!cred && !envKey) {\n if (isJsonMode()) printJson({ connected: false });\n else printInfo(\"Not connected. Run \" + c.bold(\"letter login\") + \" to set up.\");\n return;\n }\n\n if (isJsonMode()) {\n printJson({\n connected: true,\n source: envKey ? \"env\" : \"credentials\",\n project: cred?.project ?? null,\n base_url: cred?.baseUrl ?? null,\n key: envKey ? \"env\" : cred ? mask(cred.apiKey) : null,\n });\n return;\n }\n\n printSuccess(\"Connected\" + (cred ? ` to ${c.bold(cred.project.name)}` : \"\"));\n if (cred) {\n console.log(c.dim(\" Project: \") + cred.project.slug);\n console.log(c.dim(\" Key: \") + mask(cred.apiKey));\n console.log(c.dim(\" API: \") + cred.baseUrl);\n }\n if (envKey) console.log(c.dim(\" LETTER_API_KEY is set in the environment.\"));\n });\n\n auth\n .command(\"logout\")\n .description(\"Remove the stored credential (~/.letter/credentials.json)\")\n .action(async () => {\n await clearCredential();\n printSuccess(\"Removed stored credential.\");\n });\n}\n","import { Command } from \"commander\";\nimport { client } from \"../client.js\";\nimport { readCredential } from \"../config.js\";\nimport { c, isJsonMode, printError, printInfo, printJson, printSuccess, spinner } from \"../output.js\";\n\ntype StatusResponse = {\n contacts?: number;\n events?: number;\n [k: string]: unknown;\n};\n\nexport function registerStatusCommand(program: Command) {\n program\n .command(\"status\")\n .description(\"Check whether your project has received any contacts or events\")\n .action(async () => {\n const spin = spinner(\"Checking your Letter project…\").start();\n try {\n const { data } = await client.get<StatusResponse>(\"/v1/status\");\n spin.stop();\n\n if (isJsonMode()) {\n printJson(data);\n return;\n }\n\n const cred = await readCredential();\n const contacts = data.contacts ?? 0;\n const events = data.events ?? 0;\n\n if (cred) printInfo(`Project ${c.bold(cred.project.name)}`);\n if (contacts > 0 || events > 0) {\n printSuccess(`Connected. ${contacts} contact(s), ${events} event(s) received.`);\n } else {\n printInfo(\"Connected, but no data yet. Fire your first identify/track call.\");\n }\n } catch (err) {\n spin.stop();\n printError(err);\n process.exitCode = 1;\n }\n });\n}\n","import { Command } from \"commander\";\nimport {\n getBaseUrl,\n setBaseUrl,\n getConfigPath,\n resetConfig,\n} from \"../config.js\";\nimport { c, isJsonMode, printError, printJson, printSuccess } from \"../output.js\";\n\nexport function registerConfigCommands(program: Command) {\n const cfg = program.command(\"config\").description(\"Manage CLI configuration\");\n\n cfg\n .command(\"set\")\n .description(\"Set a config value\")\n .argument(\"<key>\", \"Config key: base-url\")\n .argument(\"<value>\", \"Value to set\")\n .action((key: string, value: string) => {\n switch (key) {\n case \"base-url\":\n setBaseUrl(value);\n printSuccess(`Base URL set to ${getBaseUrl()}`);\n break;\n default:\n printError(new Error(`Unknown config key: ${key}. Valid keys: base-url`));\n process.exitCode = 1;\n }\n });\n\n cfg\n .command(\"get\")\n .description(\"Show CLI configuration\")\n .argument(\"[key]\", \"Config key (omit to show all)\")\n .action((key?: string) => {\n const all = { base_url: getBaseUrl(), config_path: getConfigPath() };\n if (key && key !== \"base-url\" && key !== \"path\") {\n printError(new Error(`Unknown config key: ${key}`));\n process.exitCode = 1;\n return;\n }\n if (isJsonMode()) {\n printJson(key === \"base-url\" ? { base_url: all.base_url } : key === \"path\" ? { config_path: all.config_path } : all);\n return;\n }\n if (key === \"base-url\") console.log(all.base_url);\n else if (key === \"path\") console.log(all.config_path);\n else for (const [k, v] of Object.entries(all)) console.log(c.bold(k + \":\") + \" \" + v);\n });\n\n cfg\n .command(\"reset\")\n .description(\"Reset CLI configuration to defaults\")\n .action(() => {\n resetConfig();\n printSuccess(\"Configuration reset.\");\n });\n}\n","import { readFile } from \"node:fs/promises\";\nimport { Command } from \"commander\";\nimport { mgmt } from \"../client.js\";\nimport { resolveProjectSlug } from \"../config.js\";\nimport { emit, printError, printSuccess } from \"../output.js\";\n\n/**\n * Resource command groups for the management API. Each group wraps the same\n * REST endpoints the dashboard uses (`/v1/...`), so the whole app is operable\n * from the terminal. Everything honors the global `--json` flag (see\n * `output.emit`) and project-scoped groups take `--project <slug>` (defaulting\n * to the project connected by `letter login`).\n *\n * The shape is data-driven: one `ResourceSpec` per resource, expanded into\n * `list/get/create/update/delete` + custom verb subcommands by `register()`.\n * Keeping it declarative means a new endpoint is a few lines, not a new file.\n */\n\ntype Field = {\n /** commander option flag, e.g. \"--name <name>\". */\n flag: string;\n /** request-body key, e.g. \"name\". */\n key: string;\n /** parse the value as JSON (supports `@file.json` to read from disk). */\n json?: boolean;\n /** coerce to a number. */\n number?: boolean;\n /** coerce to a boolean. */\n boolean?: boolean;\n /** (multipart only) read the value as a file path and upload its bytes. */\n file?: boolean;\n description?: string;\n};\n\ntype Action = {\n name: string;\n describe: string;\n method: \"get\" | \"post\" | \"put\" | \"patch\" | \"delete\";\n /** Path suffix appended after the resource (and id, if `needsId`). */\n suffix: string;\n /** Whether the verb operates on a single item (default true). */\n needsId?: boolean;\n fields?: Field[];\n /** Query params for GET verbs. */\n query?: Field[];\n /** Send fields as multipart/form-data (for file uploads). */\n multipart?: boolean;\n};\n\ntype ResourceSpec = {\n /** Command group name (also the default path segment). */\n name: string;\n describe: string;\n /** Nested under /v1/projects/{slug} when true. */\n scoped: boolean;\n /** Path segment if different from `name`. */\n segment?: string;\n /** Hidden back-compat alias for the group name. */\n alias?: string;\n /** Positional id arg name (e.g. \"externalId\", \"email\"). Default \"id\". */\n idName?: string;\n list?: { paginated?: boolean; query?: Field[] };\n get?: boolean;\n create?: Field[];\n update?: Field[];\n remove?: boolean;\n actions?: Action[];\n};\n\nasync function readValue(raw: string): Promise<string> {\n if (raw.startsWith(\"@\")) return (await readFile(raw.slice(1), \"utf8\")).trim();\n return raw;\n}\n\nasync function buildBody(\n fields: Field[],\n opts: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = {};\n for (const f of fields) {\n const v = opts[camel(f.key)];\n if (v === undefined) continue;\n if (f.json) {\n body[f.key] = JSON.parse(await readValue(String(v)));\n } else if (f.number) {\n body[f.key] = Number(v);\n } else if (f.boolean) {\n body[f.key] = v === true || v === \"true\";\n } else {\n body[f.key] = await readValue(String(v));\n }\n }\n return body;\n}\n\nasync function buildForm(\n fields: Field[],\n opts: Record<string, unknown>,\n): Promise<FormData> {\n const form = new FormData();\n for (const f of fields) {\n const v = opts[camel(f.key)];\n if (v === undefined) continue;\n if (f.file) {\n const path = String(v);\n const bytes = await readFile(path);\n const name = path.split(\"/\").pop() || f.key;\n form.append(f.key, new Blob([new Uint8Array(bytes)]), name);\n } else if (f.json) {\n // The server parses these fields with JSON.parse; keep them as strings.\n form.append(f.key, await readValue(String(v)));\n } else {\n form.append(f.key, String(v));\n }\n }\n return form;\n}\n\nfunction buildQuery(\n fields: Field[],\n opts: Record<string, unknown>,\n): Record<string, string | number | undefined> {\n const q: Record<string, string | number | undefined> = {};\n for (const f of fields) {\n const v = opts[camel(f.key)];\n if (v === undefined) continue;\n q[f.key] = f.number ? Number(v) : String(v);\n }\n return q;\n}\n\n/** \"external-id\" / \"external_id\" → \"externalId\" (commander camelCases flags). */\nfunction camel(key: string): string {\n return key.replace(/[-_]([a-z])/g, (_, ch: string) => ch.toUpperCase());\n}\n\nfunction applyFields(cmd: Command, fields: Field[]) {\n for (const f of fields) {\n cmd.option(f.flag, f.description ?? f.key);\n }\n}\n\nasync function basePath(spec: ResourceSpec, project?: string): Promise<string> {\n const seg = spec.segment ?? spec.name;\n if (!spec.scoped) return `/v1/${seg}`;\n const slug = await resolveProjectSlug(project);\n return `/v1/projects/${slug}/${seg}`;\n}\n\nfunction withProject(cmd: Command, scoped: boolean): Command {\n if (scoped) {\n cmd.option(\"-p, --project <slug>\", \"Project slug (default: connected project)\");\n }\n return cmd;\n}\n\nfunction fail(err: unknown) {\n printError(err);\n process.exitCode = 1;\n}\n\nfunction register(program: Command, spec: ResourceSpec) {\n const group = program.command(spec.name).description(spec.describe);\n if (spec.alias) group.alias(spec.alias);\n const idName = spec.idName ?? \"id\";\n\n if (spec.list) {\n const cmd = withProject(\n group.command(\"list\").description(`List ${spec.name}`),\n spec.scoped,\n );\n if (spec.list.paginated) {\n cmd.option(\"--limit <n>\", \"Max items (1-100)\");\n cmd.option(\"--cursor <cursor>\", \"Pagination cursor\");\n }\n if (spec.list.query) applyFields(cmd, spec.list.query);\n cmd.action(async (opts) => {\n try {\n const query: Record<string, string | number | undefined> = {};\n if (spec.list?.paginated) {\n if (opts.limit) query.limit = Number(opts.limit);\n if (opts.cursor) query.cursor = opts.cursor;\n }\n if (spec.list?.query) Object.assign(query, buildQuery(spec.list.query, opts));\n const { data } = await mgmt.get(await basePath(spec, opts.project), query);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.get) {\n const cmd = withProject(\n group\n .command(\"get\")\n .description(`Get a ${spec.name} by ${idName}`)\n .argument(`<${idName}>`, idName),\n spec.scoped,\n );\n cmd.action(async (idValue: string, opts) => {\n try {\n const path = `${await basePath(spec, opts.project)}/${encodeURIComponent(idValue)}`;\n const { data } = await mgmt.get(path);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.create) {\n const cmd = withProject(\n group.command(\"create\").description(`Create a ${spec.name}`),\n spec.scoped,\n );\n applyFields(cmd, spec.create);\n cmd.action(async (opts) => {\n try {\n const body = await buildBody(spec.create!, opts);\n const { data } = await mgmt.post(await basePath(spec, opts.project), body);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.update) {\n const cmd = withProject(\n group\n .command(\"update\")\n .description(`Update a ${spec.name}`)\n .argument(`<${idName}>`, idName),\n spec.scoped,\n );\n applyFields(cmd, spec.update);\n cmd.action(async (idValue: string, opts) => {\n try {\n const body = await buildBody(spec.update!, opts);\n const path = `${await basePath(spec, opts.project)}/${encodeURIComponent(idValue)}`;\n const { data } = await mgmt.patch(path, body);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.remove) {\n const cmd = withProject(\n group\n .command(\"delete\")\n .description(`Delete a ${spec.name}`)\n .argument(`<${idName}>`, idName),\n spec.scoped,\n );\n cmd.action(async (idValue: string, opts) => {\n try {\n const path = `${await basePath(spec, opts.project)}/${encodeURIComponent(idValue)}`;\n await mgmt.delete(path);\n printSuccess(`Deleted ${spec.name.replace(/s$/, \"\")} ${idValue}.`);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n for (const action of spec.actions ?? []) {\n const needsId = action.needsId ?? true;\n const cmd = withProject(\n needsId\n ? group\n .command(action.name)\n .description(action.describe)\n .argument(`<${idName}>`, idName)\n : group.command(action.name).description(action.describe),\n spec.scoped,\n );\n if (action.fields) applyFields(cmd, action.fields);\n if (action.query) applyFields(cmd, action.query);\n\n const handler = async (...args: unknown[]) => {\n // commander passes (idValue?, opts, command). Last arg is the Command.\n const opts = args[args.length - 2] as Record<string, unknown>;\n const idValue = needsId ? (args[0] as string) : undefined;\n try {\n let path = await basePath(spec, opts.project as string | undefined);\n if (needsId) path += `/${encodeURIComponent(idValue!)}`;\n path += action.suffix;\n\n if (action.method === \"get\") {\n const query = action.query ? buildQuery(action.query, opts) : undefined;\n const { data } = await mgmt.get(path, query);\n emit(data);\n return;\n }\n if (action.multipart && action.fields) {\n const form = await buildForm(action.fields, opts);\n const { data, status } = await mgmt.request(\n action.method.toUpperCase(),\n path,\n { form },\n );\n if (status === 204) printSuccess(`${action.name} ok.`);\n else emit(data);\n return;\n }\n const body = action.fields ? await buildBody(action.fields, opts) : undefined;\n const { data, status } = await mgmt.request(\n action.method.toUpperCase(),\n path,\n { body },\n );\n if (status === 204) printSuccess(`${action.name} ok.`);\n else emit(data);\n } catch (err) {\n fail(err);\n }\n };\n cmd.action(handler);\n }\n}\n\nconst cursor = { paginated: true } as const;\n\nconst SPECS: ResourceSpec[] = [\n // -- Workspace ------------------------------------------------------------\n {\n name: \"projects\",\n describe: \"Manage projects\",\n scoped: false,\n idName: \"slug\",\n list: {},\n get: true,\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--timezone <tz>\", key: \"timezone\" },\n ],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--timezone <tz>\", key: \"timezone\" },\n ],\n remove: true,\n },\n {\n name: \"members\",\n describe: \"Manage workspace members\",\n scoped: false,\n idName: \"userId\",\n list: {},\n create: [\n { flag: \"--email <email>\", key: \"email\" },\n { flag: \"--role <role>\", key: \"role\", description: \"member | admin\" },\n ],\n remove: true,\n },\n {\n name: \"invitations\",\n describe: \"Manage workspace invitations\",\n scoped: false,\n list: {},\n create: [\n { flag: \"--email <email>\", key: \"email\" },\n { flag: \"--role <role>\", key: \"role\", description: \"member | admin\" },\n ],\n remove: true,\n },\n {\n // Personal access tokens (lt_pat_*) authenticate the CLI / Management API.\n // The dashboard calls these \"API keys\"; `tokens` stays as a hidden alias.\n name: \"api-keys\",\n alias: \"tokens\",\n segment: \"tokens\",\n describe: \"Manage your personal access tokens (lt_pat_*, the CLI/API credential)\",\n scoped: false,\n list: {},\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n {\n flag: \"--expires-at <iso>\",\n key: \"expires_at\",\n description: \"Expiry as an ISO 8601 datetime (default: never)\",\n },\n ],\n remove: true,\n },\n // -- Contacts & data ------------------------------------------------------\n {\n name: \"contacts\",\n describe: \"Manage contacts\",\n scoped: true,\n idName: \"externalId\",\n list: { ...cursor },\n get: true,\n actions: [\n {\n name: \"suppress\",\n describe: \"Suppress a contact\",\n method: \"post\",\n suffix: \"/suppress\",\n },\n {\n name: \"resubscribe\",\n describe: \"Resubscribe a contact\",\n method: \"post\",\n suffix: \"/resubscribe\",\n },\n {\n name: \"import\",\n describe: \"Upload a CSV import (--file <path> --mapping <json|@file>)\",\n method: \"post\",\n needsId: false,\n suffix: \"/imports\",\n multipart: true,\n fields: [\n { flag: \"--file <path>\", key: \"file\", file: true },\n { flag: \"--mapping <json>\", key: \"mapping\", json: true },\n { flag: \"--dedupe <strategy>\", key: \"dedupe\", description: \"update | skip\" },\n { flag: \"--row-count <n>\", key: \"rowCount\" },\n ],\n },\n ],\n },\n {\n name: \"accounts\",\n describe: \"Inspect accounts\",\n scoped: true,\n idName: \"externalId\",\n list: { ...cursor },\n get: true,\n },\n {\n name: \"events\",\n describe: \"List events\",\n scoped: true,\n list: { ...cursor, query: [{ flag: \"--name <name>\", key: \"name\" }] },\n },\n {\n name: \"suppressions\",\n describe: \"Manage the suppression list\",\n scoped: true,\n idName: \"email\",\n list: { ...cursor },\n create: [{ flag: \"--email <email>\", key: \"email\" }],\n remove: true,\n },\n {\n name: \"segments\",\n describe: \"Manage segments\",\n scoped: true,\n list: {},\n get: true,\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--filter <json>\", key: \"filter\", json: true },\n ],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--filter <json>\", key: \"filter\", json: true },\n ],\n remove: true,\n actions: [\n {\n name: \"preview\",\n describe: \"Count contacts matching a filter (--filter <json|@file>)\",\n method: \"post\",\n needsId: false,\n suffix: \"/preview\",\n fields: [{ flag: \"--filter <json>\", key: \"filter\", json: true }],\n },\n ],\n },\n // -- Sequences ------------------------------------------------------------\n {\n name: \"sequences\",\n describe: \"Manage drip sequences\",\n scoped: true,\n list: {},\n get: true,\n create: [{ flag: \"--name <name>\", key: \"name\" }],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--status <status>\", key: \"status\", description: \"active | archived\" },\n ],\n remove: true,\n actions: [\n {\n name: \"draft\",\n describe: \"Save the draft graph + trigger\",\n method: \"put\",\n suffix: \"/draft\",\n fields: [\n { flag: \"--graph <json>\", key: \"graph\", json: true },\n { flag: \"--trigger <json>\", key: \"trigger\", json: true },\n { flag: \"--expected-revision <n>\", key: \"expected_revision\", number: true },\n ],\n },\n {\n name: \"publish\",\n describe: \"Publish the current draft\",\n method: \"post\",\n suffix: \"/publish\",\n },\n {\n name: \"preview\",\n describe: \"Render an email node (--node-id <id>)\",\n method: \"post\",\n suffix: \"/preview\",\n fields: [{ flag: \"--node-id <id>\", key: \"nodeId\" }],\n },\n {\n name: \"test-email\",\n describe: \"Send a test email (--node-id <id> --to <email>)\",\n method: \"post\",\n suffix: \"/test-email\",\n fields: [\n { flag: \"--node-id <id>\", key: \"nodeId\" },\n { flag: \"--to <email>\", key: \"to\" },\n ],\n },\n {\n name: \"activity\",\n describe: \"Show enrollment activity\",\n method: \"get\",\n suffix: \"/activity\",\n },\n {\n name: \"versions\",\n describe: \"List published versions\",\n method: \"get\",\n suffix: \"/versions\",\n },\n ],\n },\n // -- Broadcasts -----------------------------------------------------------\n {\n name: \"broadcasts\",\n describe: \"Manage broadcasts\",\n scoped: true,\n list: {},\n get: true,\n create: [{ flag: \"--name <name>\", key: \"name\" }],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--subject <subject>\", key: \"subject\" },\n { flag: \"--preview <preview>\", key: \"preview\" },\n { flag: \"--audience <json>\", key: \"audience\", json: true },\n { flag: \"--template-id <id>\", key: \"template_id\" },\n { flag: \"--body-doc <json>\", key: \"body_doc\", json: true },\n ],\n remove: true,\n actions: [\n {\n name: \"preflight\",\n describe: \"Validate before sending\",\n method: \"post\",\n suffix: \"/preflight\",\n },\n {\n name: \"schedule\",\n describe: \"Schedule (--scheduled-at <iso>) or send now (omit)\",\n method: \"post\",\n suffix: \"/schedule\",\n fields: [{ flag: \"--scheduled-at <iso>\", key: \"scheduled_at\" }],\n },\n {\n name: \"cancel\",\n describe: \"Cancel a scheduled or sending broadcast\",\n method: \"post\",\n suffix: \"/cancel\",\n },\n {\n name: \"live\",\n describe: \"Live status, stats, and recent activity\",\n method: \"get\",\n suffix: \"/live\",\n },\n ],\n },\n // -- Templates ------------------------------------------------------------\n {\n name: \"templates\",\n describe: \"Manage email templates\",\n scoped: true,\n list: {},\n get: true,\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--design <json>\", key: \"design\", json: true },\n ],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--design <json>\", key: \"design\", json: true },\n ],\n remove: true,\n actions: [\n {\n name: \"default\",\n describe: \"Set as the project default\",\n method: \"post\",\n suffix: \"/default\",\n },\n {\n name: \"reset\",\n describe: \"Reset design to a preset (--preset plain|branded)\",\n method: \"post\",\n suffix: \"/reset\",\n fields: [{ flag: \"--preset <preset>\", key: \"preset\" }],\n },\n {\n name: \"logo\",\n describe: \"Upload a logo (--file <path>)\",\n method: \"post\",\n suffix: \"/logo\",\n multipart: true,\n fields: [{ flag: \"--file <path>\", key: \"file\", file: true }],\n },\n {\n name: \"remove-logo\",\n describe: \"Remove the template logo\",\n method: \"delete\",\n suffix: \"/logo\",\n },\n ],\n },\n // -- Settings -------------------------------------------------------------\n {\n name: \"domains\",\n describe: \"Manage sending domains\",\n scoped: true,\n list: {},\n create: [{ flag: \"--domain <domain>\", key: \"domain\" }],\n remove: true,\n actions: [\n {\n name: \"verify\",\n describe: \"Re-check DKIM verification\",\n method: \"post\",\n suffix: \"/verify\",\n },\n ],\n },\n {\n // Project ingestion keys (lt_live_*) authenticate the SDKs / ingestion API.\n // The dashboard calls these \"Project tokens\"; `keys` stays as a hidden alias.\n name: \"project-tokens\",\n alias: \"keys\",\n segment: \"keys\",\n describe: \"Manage project ingestion keys (lt_live_*, the SDK/ingestion credential)\",\n scoped: true,\n list: {},\n create: [{ flag: \"--name <name>\", key: \"name\" }],\n remove: true,\n },\n];\n\nexport function registerResourceCommands(program: Command) {\n for (const spec of SPECS) register(program, spec);\n\n // `letter me` — the token's identity (handy sanity check).\n program\n .command(\"me\")\n .description(\"Show the token's user, workspace, and role\")\n .action(async () => {\n try {\n const { data } = await mgmt.get(\"/v1/me\");\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n\n // Sender identity + sending mode don't fit the CRUD mold: register directly.\n program\n .command(\"sender-identity\")\n .description(\"Set From / From-name / Reply-To (--from-email, --from-name, --reply-to-email)\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .option(\"--from-email <email>\", \"From address\")\n .option(\"--from-name <name>\", \"From display name\")\n .option(\"--reply-to-email <email>\", \"Reply-To address\")\n .action(async (opts) => {\n try {\n const slug = await resolveProjectSlug(opts.project);\n const body: Record<string, unknown> = {};\n if (opts.fromEmail !== undefined) body.from_email = opts.fromEmail;\n if (opts.fromName !== undefined) body.from_name = opts.fromName;\n if (opts.replyToEmail !== undefined) body.reply_to_email = opts.replyToEmail;\n const { data } = await mgmt.put(`/v1/projects/${slug}/sender-identity`, body);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n\n program\n .command(\"sending-mode\")\n .description(\"Set the sending mode (letter_subdomain | byo_domain)\")\n .argument(\"<mode>\", \"letter_subdomain | byo_domain\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .action(async (mode: string, opts) => {\n try {\n const slug = await resolveProjectSlug(opts.project);\n const { data } = await mgmt.put(`/v1/projects/${slug}/sending-mode`, {\n sending_mode: mode,\n });\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n}\n"],"mappings":"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","names":["path","path","readFile","writeFile","spawn","readFile","readFile","spawn","program","program","program","program","readFile","readFile","path","program","data","status"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterapp/cli",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Letter CLI - connect your app to Letter in one command. Interactive, secure device login: no API key ever touches your shell or chat.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://letter.app",
@@ -30,19 +30,28 @@
30
30
  "cli",
31
31
  "analytics",
32
32
  "email",
33
- "onboarding"
33
+ "onboarding",
34
+ "ai-agents"
34
35
  ],
35
36
  "publishConfig": {
36
37
  "access": "public"
37
38
  },
38
39
  "scripts": {
39
- "build": "tsc -p tsconfig.json",
40
- "clean": "rm -rf dist tsconfig.tsbuildinfo",
40
+ "dev": "tsup --watch",
41
+ "build": "tsup",
42
+ "start": "node dist/index.js",
41
43
  "typecheck": "tsc --noEmit",
42
- "prepublishOnly": "npm run clean && npm run build"
44
+ "smoke:cli": "node scripts/smoke-cli.mjs"
45
+ },
46
+ "dependencies": {
47
+ "chalk": "^5.4.1",
48
+ "commander": "^13.1.0",
49
+ "conf": "^13.1.0",
50
+ "ora": "^8.2.0"
43
51
  },
44
52
  "devDependencies": {
45
53
  "@types/node": "^22.10.2",
54
+ "tsup": "^8.4.0",
46
55
  "typescript": "^5.7.2"
47
56
  }
48
57
  }
@@ -1,151 +0,0 @@
1
- import { parseArgs, flagString, flagBool } from "../lib/args.js";
2
- import { resolveApiBase, DEFAULT_API_BASE, saveCredential } from "../lib/config.js";
3
- import { startDeviceAuth, pollDeviceAuth } from "../lib/api.js";
4
- import { openUrl } from "../lib/browser.js";
5
- import { upsertEnv } from "../lib/env-file.js";
6
- import { detectFramework, detectPackageManager, installCommand, runInstall, } from "../lib/pm.js";
7
- import { banner, color, error, info, log, prompt, success, warn, } from "../lib/ui.js";
8
- const SDK_PACKAGE = "@letterapp/node";
9
- const ENV_FILE = ".env.local";
10
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
11
- /**
12
- * `letter login` / `letter init` (the default command). Runs the interactive
13
- * device-authorization flow, writes the minted key to the project env + the
14
- * shared credential store, and installs the SDK. The API key is NEVER printed.
15
- */
16
- export async function runLogin(argv) {
17
- const { flags } = parseArgs(argv);
18
- const base = resolveApiBase(flagString(flags, "base-url"));
19
- const cwd = process.cwd();
20
- const autoYes = flagBool(flags, "yes") === true || flags.y === true;
21
- const allowOpen = flagBool(flags, "open") !== false; // --no-open disables
22
- const doInstall = flagBool(flags, "install") !== false; // --no-install disables
23
- const ciKey = flagString(flags, "api-key");
24
- banner();
25
- // CI escape hatch: write the provided key non-interactively. Documented as
26
- // for automation only - interactive/agent use should rely on the device flow
27
- // so no secret is passed through the command line / chat.
28
- if (ciKey) {
29
- const entries = { LETTER_API_KEY: ciKey };
30
- if (base !== DEFAULT_API_BASE)
31
- entries.LETTER_BASE_URL = base;
32
- const file = await upsertEnv(cwd, ENV_FILE, entries);
33
- success(`Saved LETTER_API_KEY to ${rel(cwd, file)} (--api-key).`);
34
- warn("--api-key is for CI. For interactive setup, run `letter login`.");
35
- return 0;
36
- }
37
- // 1. Begin the flow.
38
- let flow;
39
- try {
40
- flow = await startDeviceAuth(base);
41
- }
42
- catch (err) {
43
- error(err.message);
44
- return 1;
45
- }
46
- log(`${color.bold("Confirm this code in your browser:")}`);
47
- log();
48
- log(` ${color.cyan(color.bold(flow.user_code))}`);
49
- log();
50
- // 2. Open the browser (interactive: wait for Enter; otherwise auto/print).
51
- const interactive = process.stdin.isTTY && !autoYes;
52
- if (interactive && allowOpen) {
53
- await prompt(color.dim("Press Enter to open your browser… "));
54
- }
55
- if (allowOpen)
56
- openUrl(flow.verification_uri_complete);
57
- info(`If your browser didn't open, visit:`);
58
- log(` ${color.blue(flow.verification_uri_complete)}`);
59
- log();
60
- info("Waiting for you to approve… (Ctrl+C to cancel)");
61
- // 3. Poll until the user approves/denies or the code expires.
62
- const deadline = Date.now() + flow.expires_in * 1000;
63
- let intervalMs = Math.max(1, flow.interval) * 1000;
64
- while (Date.now() < deadline) {
65
- await sleep(intervalMs);
66
- let res;
67
- try {
68
- res = await pollDeviceAuth(base, flow.device_code);
69
- }
70
- catch (err) {
71
- error(err.message);
72
- return 1;
73
- }
74
- if (res.status === "authorization_pending")
75
- continue;
76
- if (res.status === "slow_down") {
77
- intervalMs += 1000;
78
- continue;
79
- }
80
- if (res.status === "access_denied") {
81
- error("Request denied in the browser. Nothing was changed.");
82
- return 1;
83
- }
84
- if (res.status === "expired_token") {
85
- error("This login expired. Run the command again to retry.");
86
- return 1;
87
- }
88
- // Approved.
89
- return finish(res.api_key, res.base_url, res.project, cwd, doInstall);
90
- }
91
- error("Timed out waiting for approval. Run the command again to retry.");
92
- return 1;
93
- }
94
- async function finish(apiKey, baseUrl, project, cwd, doInstall) {
95
- log();
96
- success(`Approved for project ${color.bold(project.name)}.`);
97
- // Write the key to the project env (value never printed) + shared store.
98
- const entries = { LETTER_API_KEY: apiKey };
99
- if (baseUrl && baseUrl !== DEFAULT_API_BASE)
100
- entries.LETTER_BASE_URL = baseUrl;
101
- const envFile = await upsertEnv(cwd, ENV_FILE, entries);
102
- success(`Saved LETTER_API_KEY to ${rel(cwd, envFile)}.`);
103
- try {
104
- const credFile = await saveCredential({
105
- apiKey,
106
- baseUrl: baseUrl || DEFAULT_API_BASE,
107
- project,
108
- savedAt: new Date().toISOString(),
109
- });
110
- success(`Stored credentials in ${tildify(credFile)} for tooling (MCP).`);
111
- }
112
- catch {
113
- warn("Could not write ~/.letter/credentials.json (continuing).");
114
- }
115
- // Install the SDK.
116
- const pm = await detectPackageManager(cwd);
117
- if (doInstall) {
118
- info(`Installing ${SDK_PACKAGE} with ${pm}…`);
119
- const code = await runInstall(pm, SDK_PACKAGE, cwd);
120
- if (code === 0)
121
- success(`Installed ${SDK_PACKAGE}.`);
122
- else
123
- warn(`Install failed. Run: ${installCommand(pm, SDK_PACKAGE)}`);
124
- }
125
- else {
126
- info(`Skipped install. Run: ${installCommand(pm, SDK_PACKAGE)}`);
127
- }
128
- const framework = await detectFramework(cwd);
129
- printNextSteps(framework);
130
- return 0;
131
- }
132
- function printNextSteps(framework) {
133
- log();
134
- log(color.bold("Next steps"));
135
- if (framework)
136
- log(color.dim(`Detected ${framework}.`));
137
- log(` 1. Create a server-side client that reads ${color.cyan("process.env.LETTER_API_KEY")}.`);
138
- log(` 2. Call ${color.cyan("letter.identify(...)")} where users sign up or log in.`);
139
- log(` 3. Call ${color.cyan("letter.track(...)")} on 2-3 key actions.`);
140
- log();
141
- log(color.dim("Full guide: https://letter.app/docs/agent-setup"));
142
- log(color.dim("Your API key is in .env.local - keep it out of source control."));
143
- log();
144
- }
145
- function rel(cwd, file) {
146
- return file.startsWith(cwd) ? file.slice(cwd.length + 1) || file : file;
147
- }
148
- function tildify(file) {
149
- const home = process.env.HOME || process.env.USERPROFILE;
150
- return home && file.startsWith(home) ? `~${file.slice(home.length)}` : file;
151
- }
@@ -1,21 +0,0 @@
1
- import { runLogin } from "./login.js";
2
- /**
3
- * Command registry. v1 ships only the device-login setup flow (as `login`,
4
- * `init`, and the default), but the shape here is the extension point: future
5
- * authenticated subcommands (`sequences`, `broadcast`, `contacts`, `events`,
6
- * `keys`, `status`) register the same way and reuse the credential store in
7
- * lib/config.ts + the API client in lib/api.ts. See README "Roadmap".
8
- */
9
- export const COMMANDS = [
10
- {
11
- name: "login",
12
- aliases: ["init"],
13
- summary: "Connect this project to Letter (interactive device login)",
14
- run: runLogin,
15
- },
16
- ];
17
- /** The command that runs when none is given. */
18
- export const DEFAULT_COMMAND = "login";
19
- export function findCommand(name) {
20
- return COMMANDS.find((c) => c.name === name || c.aliases?.includes(name));
21
- }
package/dist/lib/api.js DELETED
@@ -1,29 +0,0 @@
1
- const USER_AGENT = "@letterapp/cli";
2
- /** Starts a device-authorization flow against the given API base. */
3
- export async function startDeviceAuth(base) {
4
- const res = await fetch(`${base}/v1/cli/auth/start`, {
5
- method: "POST",
6
- headers: { "content-type": "application/json", "user-agent": USER_AGENT },
7
- body: "{}",
8
- });
9
- if (!res.ok) {
10
- throw new Error(`Could not start login (HTTP ${res.status}). Is ${base} reachable?`);
11
- }
12
- return (await res.json());
13
- }
14
- /** Polls once for approval. */
15
- export async function pollDeviceAuth(base, deviceCode) {
16
- const res = await fetch(`${base}/v1/cli/auth/poll`, {
17
- method: "POST",
18
- headers: { "content-type": "application/json", "user-agent": USER_AGENT },
19
- body: JSON.stringify({ device_code: deviceCode }),
20
- });
21
- if (res.status === 429) {
22
- const retryAfter = Number(res.headers.get("retry-after") ?? "5");
23
- return { status: "slow_down", retryAfter };
24
- }
25
- if (!res.ok) {
26
- throw new Error(`Login poll failed (HTTP ${res.status}).`);
27
- }
28
- return (await res.json());
29
- }
package/dist/lib/args.js DELETED
@@ -1,49 +0,0 @@
1
- export function parseArgs(argv) {
2
- const positionals = [];
3
- const flags = {};
4
- for (let i = 0; i < argv.length; i++) {
5
- const arg = argv[i];
6
- if (arg.startsWith("--")) {
7
- const body = arg.slice(2);
8
- const eq = body.indexOf("=");
9
- if (eq !== -1) {
10
- flags[body.slice(0, eq)] = body.slice(eq + 1);
11
- }
12
- else if (body.startsWith("no-")) {
13
- flags[body.slice(3)] = false;
14
- }
15
- else {
16
- const next = argv[i + 1];
17
- if (next && !next.startsWith("-")) {
18
- flags[body] = next;
19
- i++;
20
- }
21
- else {
22
- flags[body] = true;
23
- }
24
- }
25
- }
26
- else if (arg.startsWith("-") && arg.length > 1) {
27
- for (const ch of arg.slice(1))
28
- flags[ch] = true;
29
- }
30
- else {
31
- positionals.push(arg);
32
- }
33
- }
34
- return { positionals, flags };
35
- }
36
- export function flagString(flags, ...names) {
37
- for (const n of names) {
38
- const v = flags[n];
39
- if (typeof v === "string")
40
- return v;
41
- }
42
- return undefined;
43
- }
44
- export function flagBool(flags, name) {
45
- const v = flags[name];
46
- if (typeof v === "boolean")
47
- return v;
48
- return undefined;
49
- }
@@ -1,33 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- /**
3
- * Opens `url` in the default browser. Best-effort and non-blocking: if it fails
4
- * (headless box, no DISPLAY) the caller has already printed the URL so the user
5
- * can open it manually. Returns true if a launcher was spawned.
6
- */
7
- export function openUrl(url) {
8
- const platform = process.platform;
9
- let command;
10
- let args;
11
- if (platform === "darwin") {
12
- command = "open";
13
- args = [url];
14
- }
15
- else if (platform === "win32") {
16
- command = "cmd";
17
- // `start` needs an empty title arg; the comma-free form avoids quoting woes.
18
- args = ["/c", "start", "", url];
19
- }
20
- else {
21
- command = "xdg-open";
22
- args = [url];
23
- }
24
- try {
25
- const child = spawn(command, args, { stdio: "ignore", detached: true });
26
- child.on("error", () => { });
27
- child.unref();
28
- return true;
29
- }
30
- catch {
31
- return false;
32
- }
33
- }
@@ -1,35 +0,0 @@
1
- import { homedir } from "node:os";
2
- import path from "node:path";
3
- import { mkdir, readFile, writeFile } from "node:fs/promises";
4
- /** The SDK's built-in default; when the resolved base equals this we don't
5
- * write LETTER_BASE_URL to the project env. */
6
- export const DEFAULT_API_BASE = "https://api.letter.app";
7
- /** Resolve the API base: explicit flag > env > prod default. */
8
- export function resolveApiBase(flagValue) {
9
- const raw = flagValue || process.env.LETTER_BASE_URL || DEFAULT_API_BASE;
10
- return raw.replace(/\/$/, "");
11
- }
12
- function credentialsPath() {
13
- return path.join(homedir(), ".letter", "credentials.json");
14
- }
15
- /**
16
- * Persists the credential to ~/.letter/credentials.json with owner-only
17
- * permissions. Read by tools like @letterapp/mcp so the secret never has to be
18
- * pasted into an MCP config.
19
- */
20
- export async function saveCredential(cred) {
21
- const file = credentialsPath();
22
- await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
23
- await writeFile(file, `${JSON.stringify(cred, null, 2)}\n`, { mode: 0o600 });
24
- return file;
25
- }
26
- /** Reads the stored credential, or null if none. */
27
- export async function readCredential() {
28
- try {
29
- const raw = await readFile(credentialsPath(), "utf8");
30
- return JSON.parse(raw);
31
- }
32
- catch {
33
- return null;
34
- }
35
- }