@videan/cli 0.5.1 → 0.5.3

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.
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts", "../src/commands/login.ts", "../../shared/src/constants.ts", "../../shared/src/detect.ts", "../../shared/src/sizing.ts", "../../shared/src/pricing.ts", "../../sdk/src/client.ts", "../../shared/src/types.ts", "../../shared/src/constants.ts", "../../shared/src/detect.ts", "../../shared/src/sizing.ts", "../../shared/src/pricing.ts", "../../shared/src/visibility.ts", "../src/config.ts", "../src/prompt.ts", "../src/browser-login.ts", "../src/commands/logout.ts", "../src/commands/init.ts", "../src/videan-md.ts", "../src/commands/deploy.ts", "../src/util.ts", "../src/commands/pull.ts", "../src/commands/logs.ts", "../src/commands/metrics.ts", "../src/commands/rollback.ts", "../src/commands/secrets.ts", "../src/commands/apps.ts", "../src/commands/status.ts", "../src/commands/keys.ts", "../src/commands/orgs.ts", "../src/commands/storage.ts", "../src/commands/email.ts", "../src/commands/jobs.ts", "../src/commands/usage.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { loginCommand } from \"./commands/login.js\";\nimport { logoutCommand } from \"./commands/logout.js\";\nimport { initCommand } from \"./commands/init.js\";\nimport { deployCommand } from \"./commands/deploy.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { logsCommand } from \"./commands/logs.js\";\nimport { metricsCommand } from \"./commands/metrics.js\";\nimport { rollbackCommand } from \"./commands/rollback.js\";\nimport { secretsCommand } from \"./commands/secrets.js\";\nimport { appsCommand } from \"./commands/apps.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { keysCommand } from \"./commands/keys.js\";\nimport { orgsCommand } from \"./commands/orgs.js\";\nimport { storageCommand } from \"./commands/storage.js\";\nimport { emailCommand } from \"./commands/email.js\";\nimport { jobsCommand } from \"./commands/jobs.js\";\nimport { usageCommand } from \"./commands/usage.js\";\nimport { installPromptCrashGuard } from \"./prompt.js\";\n\ninstallPromptCrashGuard();\n\nconst program = new Command();\n\nprogram\n .name(\"videan\")\n .description(\"Videan CLI \u2014 deploy apps in seconds\")\n // Keep in step with package.json on every release: this is what\n // `videan --version` prints, and a support report quoting a stale number\n // sends the investigation to the wrong code.\n .version(\"0.5.1\");\n\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(initCommand);\nprogram.addCommand(deployCommand);\nprogram.addCommand(pullCommand);\nprogram.addCommand(logsCommand);\nprogram.addCommand(metricsCommand);\nprogram.addCommand(rollbackCommand);\nprogram.addCommand(secretsCommand);\nprogram.addCommand(appsCommand);\nprogram.addCommand(statusCommand);\nprogram.addCommand(keysCommand);\nprogram.addCommand(orgsCommand);\nprogram.addCommand(storageCommand);\nprogram.addCommand(emailCommand);\nprogram.addCommand(jobsCommand);\nprogram.addCommand(usageCommand);\n\n/**\n * Commander does not await an async action's rejection, so anything a command\n * throws lands as an unhandled rejection and node prints a stack trace with\n * `triggerUncaughtException` at the top of it. That is what a user saw for a\n * connection reset. `parseAsync` gives the rejection somewhere to go; the\n * handler is the backstop for a throw outside a command's own promise.\n */\nfunction die(e: any): never {\n console.error(`Error: ${e?.message || e}`);\n // Only when asked: the message is the actionable part, and a stack trace on\n // every network blip trains people to ignore CLI output.\n if (process.env.VIDEAN_DEBUG && e?.stack) console.error(e.stack);\n process.exit(1);\n}\n\nprocess.on(\"unhandledRejection\", die);\n\nprogram.parseAsync().catch(die);\n", "import { Command } from \"commander\";\nimport inquirer from \"inquirer\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport os from \"node:os\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { DEFAULT_API_URL } from \"@cloud/shared\";\nimport { setApiKey, setDefaultOrg, getBaseUrl, setBaseUrl } from \"../config.js\";\nimport { prompt } from \"../prompt.js\";\nimport {\n consoleOriginFor,\n createCallbackServer,\n openBrowser,\n randomState,\n} from \"../browser-login.js\";\n\n/**\n * Finishes a password sign-in that answered `requires_2fa`.\n *\n * The password checked out but no token was issued: the account wants the\n * 6-digit code the API just emailed, redeemed against the opaque challenge\n * together via POST /auth/login/2fa. The server allows 5 attempts per\n * challenge and the code lives 10 minutes, so wrong codes re-prompt (with the\n * server's own message) rather than aborting -- a typo shouldn't cost a whole\n * new sign-in and a second email.\n *\n * Never reached on a non-TTY without a clean exit: the code arrives by email,\n * so there is nothing a flag or env var could supply -- non-interactive\n * callers should use VIDEAN_API_KEY instead of login (CLI convention: closed\n * stdin must refuse, not hang or crash).\n */\nexport async function completeTwoFactor(\n spinner: ReturnType<typeof ora>,\n baseUrl: string,\n login: { challenge?: string; code_email_sent?: boolean },\n): Promise<string> {\n if (!process.stdin.isTTY) {\n spinner.fail(\n \"This account has two-factor enabled: login needs a terminal to type the emailed code. \" +\n \"Non-interactively, set VIDEAN_API_KEY instead (create a key from a terminal or the dashboard).\",\n );\n process.exit(1);\n }\n\n spinner.stop();\n console.log(\n login.code_email_sent === false\n ? chalk.yellow(\"Two-factor is on, but the code email could not be sent. Trying anyway \u2014 check your inbox.\")\n : \"A 6-digit sign-in code was emailed to you (it expires in 10 minutes).\",\n );\n\n // The server spends an attempt per wrong code and caps at 5 per challenge;\n // stopping one short locally keeps the last server answer readable instead\n // of a generic \"too many wrong codes\" after the counter is already burned.\n for (let attempt = 0; attempt < 4; attempt++) {\n const { code } = await prompt<{ code: string }>(\n [\n {\n type: \"input\",\n name: \"code\",\n message: \"Sign-in code:\",\n validate: (v: string) => (/^\\d{6}$/.test(v.trim()) ? true : \"The code is 6 digits\"),\n },\n ],\n \"Two-factor login cannot be completed non-interactively; set VIDEAN_API_KEY instead.\",\n );\n\n let response: Response;\n try {\n response = await fetch(`${baseUrl}/auth/login/2fa`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ challenge: login.challenge, code: code.trim() }),\n });\n } catch (e: any) {\n spinner.fail(`Login failed: could not reach ${baseUrl} (${e?.message || e})`);\n process.exit(1);\n }\n\n if (response.ok) {\n const data = (await response.json()) as { data: { token: string } };\n return data.data.token;\n }\n\n const body = (await response.json().catch(() => null)) as\n | { error?: { message?: string } }\n | null;\n console.error(chalk.red(body?.error?.message || \"Invalid or expired sign-in code\"));\n }\n\n spinner.fail(\"Too many wrong codes. Run `videan login` again to get a new one.\");\n process.exit(1);\n}\n\n/**\n * Picks the org to scope the CLI to. Shared by every flow: an explicit --org is\n * verified rather than trusted (a key scoped to an org the user is not in fails\n * on every later call with no hint as to why), several orgs prompt on a TTY and\n * refuse off one, a single org is taken as-is.\n */\nasync function pickOrg(\n spinner: ReturnType<typeof ora>,\n session: CloudClient,\n orgOpt: string | undefined,\n resume: string,\n): Promise<string | undefined> {\n let orgs;\n try {\n orgs = await session.orgs.list();\n } catch (e: any) {\n spinner.fail(`Logged in, but could not list organizations: ${e?.message}`);\n process.exit(1);\n }\n\n if (orgOpt) {\n const match = orgs.find((o) => o.id === orgOpt || o.name === orgOpt);\n if (!match) {\n spinner.fail(`No organization ${orgOpt} for this account.`);\n process.exit(1);\n }\n return match.id;\n }\n if (orgs.length > 1 && !process.stdin.isTTY) {\n spinner.fail(`Several organizations (${orgs.map((o) => o.id).join(\", \")}). Pass --org <id>.`);\n process.exit(1);\n }\n if (orgs.length > 1) {\n spinner.stop();\n const pick = await inquirer.prompt([\n {\n type: \"list\",\n name: \"orgId\",\n message: \"Organization:\",\n choices: orgs.map((o) => ({ name: `${o.name} (${o.id})`, value: o.id })),\n },\n ]);\n spinner.start(resume);\n return pick.orgId;\n }\n // One org, or none yet -- the caller handles the empty case.\n return orgs[0]?.id;\n}\n\n/**\n * The common ending: trade a session token for an API key and save it.\n *\n * The login JWT expires in days. Trading it for a key keeps the CLI working,\n * and a stolen config file can then be revoked without a password change.\n * Requires an org, since keys are scoped to one.\n */\nasync function finishWithToken(\n spinner: ReturnType<typeof ora>,\n baseUrl: string,\n token: string,\n orgOpt: string | undefined,\n): Promise<void> {\n const session = new CloudClient({ apiKey: token, baseUrl });\n const orgId = await pickOrg(spinner, session, orgOpt, \"Creating CLI key...\");\n\n if (!orgId) {\n // Nothing to scope a key to yet; the session token still works for the\n // one call needed to create an org.\n setApiKey(token);\n spinner.warn(\n chalk.yellow(\"Logged in, but you have no organization yet. Run `videan orgs create <name>`.\"),\n );\n return;\n }\n\n try {\n const key = await session.apiKeys.create({\n name: `cli@${os.hostname()}`,\n orgId,\n });\n setApiKey(key.key);\n setDefaultOrg(orgId);\n spinner.succeed(chalk.green(\"Logged in.\"));\n console.log(chalk.gray(` Org: ${orgId}`));\n console.log(chalk.gray(` Key: ${key.keyPrefix}\u2026 (revoke with \\`videan keys revoke ${key.id}\\`)`));\n } catch (e: any) {\n // Fall back to the session token rather than leaving the user logged out.\n setApiKey(token);\n setDefaultOrg(orgId);\n spinner.warn(\n chalk.yellow(`Logged in with a temporary session (key creation failed: ${e?.message}).`),\n );\n }\n}\n\n/**\n * Browser sign-in: listen on a loopback port, send the user to the console's\n * /cli-auth page, and wait for it to deliver a session token. This is the only\n * login the CLI can offer an account with no password \u2014 Google- and SSO-only\n * accounts \u2014 and it is the default on a terminal because of that.\n */\nasync function browserLogin(consoleOrigin: string): Promise<string> {\n const state = randomState();\n const server = await createCallbackServer(state, undefined, consoleOrigin);\n const url = `${consoleOrigin}/cli-auth?port=${server.port}&state=${state}`;\n\n console.log(\"Opening your browser to sign in (password, Google or SSO all work):\");\n console.log(chalk.cyan(` ${url}`));\n console.log(chalk.gray(\" Waiting for the browser\u2026 (Ctrl-C to cancel)\"));\n openBrowser(url);\n\n return server.waitForToken;\n}\n\nasync function passwordLogin(\n spinner: ReturnType<typeof ora>,\n baseUrl: string,\n email: string,\n password: string,\n): Promise<string> {\n let login: {\n token?: string;\n requires_2fa?: boolean;\n challenge?: string;\n code_email_sent?: boolean;\n };\n try {\n const response = await fetch(`${baseUrl}/auth/login`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, password }),\n });\n\n if (!response.ok) {\n // Only 401 means the credentials are wrong. Everything else is the CLI\n // talking to the wrong place, and blaming the password for it sends the\n // user off resetting a password that was never the problem -- a stale\n // saved `baseUrl` pointing at a host that no longer serves /v1 reported\n // \"check your credentials\" for a plain 404.\n if (response.status === 401) {\n spinner.fail(\"Login failed. Check your credentials.\");\n } else {\n spinner.fail(`Login failed: ${response.status} from ${baseUrl}/auth/login`);\n console.error(\n chalk.gray(\" Wrong API URL? Re-run with --api-url <url>, or `videan logout` to reset.\"),\n );\n }\n process.exit(1);\n }\n\n const data = (await response.json()) as {\n data: {\n token?: string;\n requires_2fa?: boolean;\n challenge?: string;\n code_email_sent?: boolean;\n };\n };\n login = data.data;\n } catch (e: any) {\n spinner.fail(`Login failed: could not reach ${baseUrl} (${e?.message || e})`);\n process.exit(1);\n }\n\n if (login.requires_2fa) {\n return completeTwoFactor(spinner, baseUrl, login);\n }\n return login.token!;\n}\n\nexport const loginCommand = new Command(\"login\")\n .description(\"Log in to Cloud (opens your browser; --email/--password for the prompt flow)\")\n .option(\"-e, --email <email>\", \"Email \u2014 switches to the email/password flow\")\n .option(\"-p, --password <password>\", \"Password (skips the prompt; prefer VIDEAN_PASSWORD)\")\n .option(\"-o, --org <id>\", \"Organization to scope the key to (skips the picker)\")\n .option(\"-k, --key <api-key>\", \"Use an existing API key instead of signing in\")\n .option(\"--no-browser\", \"Never open a browser; prompt for email and password\")\n .option(\"--api-url <url>\", \"API base URL to use and remember (default: the public API)\")\n .action(async (opts) => {\n // Same base as the SDK uses, so a custom baseUrl works for both. Saved\n // before the request, so that a wrong --api-url can be corrected by another\n // login rather than by editing the config file by hand.\n if (opts.apiUrl) setBaseUrl(opts.apiUrl.replace(/\\/$/, \"\"));\n const baseUrl = (getBaseUrl() || DEFAULT_API_URL).replace(/\\/$/, \"\");\n\n // --key: no sign-in at all. Verify the key actually works before saving it,\n // otherwise every later command fails with no hint that login was the problem.\n if (opts.key) {\n const spinner = ora(\"Checking the key...\").start();\n const session = new CloudClient({ apiKey: opts.key, baseUrl });\n const orgId = await pickOrg(spinner, session, opts.org, \"Saving...\");\n setApiKey(opts.key);\n if (orgId) setDefaultOrg(orgId);\n spinner.succeed(chalk.green(\"Logged in with the provided key.\"));\n if (orgId) console.log(chalk.gray(` Org: ${orgId}`));\n return;\n }\n\n const password = opts.password || process.env.VIDEAN_PASSWORD || process.env.CLOUD_PASSWORD;\n const wantsPasswordFlow = Boolean(opts.email || password || opts.browser === false);\n const consoleOrigin = consoleOriginFor(baseUrl);\n\n // Browser flow is the default on a terminal: it is the only login that\n // works for Google- and SSO-only accounts, and the password flow is one\n // flag away. A base URL whose console we cannot derive (localhost, a bare\n // IP) falls back to the prompt flow rather than opening a wrong page.\n if (!wantsPasswordFlow && process.stdin.isTTY && consoleOrigin) {\n let token: string;\n try {\n token = await browserLogin(consoleOrigin);\n } catch (e: any) {\n console.error(chalk.red(`Browser sign-in failed: ${e?.message || e}`));\n console.error(chalk.gray(\" For the prompt flow instead: videan login --no-browser\"));\n process.exit(1);\n }\n const spinner = ora(\"Finishing up...\").start();\n await finishWithToken(spinner, baseUrl, token, opts.org);\n return;\n }\n\n // Anything not supplied has to be prompted for, and prompting needs a\n // terminal. Without this the command hung on a closed stdin and then died\n // inside readline with `ERR_USE_AFTER_CLOSE` -- an agent driving the CLI, the\n // platform's whole intended caller, could not log in and could not tell why.\n if ((!opts.email || !password) && !process.stdin.isTTY) {\n console.error(\n chalk.red(\n \"login needs a terminal to prompt. Non-interactively, pass --email and --password \" +\n \"(or VIDEAN_PASSWORD), pass --key <api-key>, or skip login entirely by setting VIDEAN_API_KEY.\",\n ),\n );\n process.exit(1);\n }\n\n const prompts = [\n ...(opts.email ? [] : [{ type: \"input\", name: \"email\", message: \"Email:\" }]),\n ...(password ? [] : [{ type: \"password\", name: \"password\", message: \"Password:\" }]),\n ];\n const answers = {\n email: opts.email,\n password,\n ...(prompts.length ? await inquirer.prompt(prompts) : {}),\n };\n\n const spinner = ora(\"Logging in...\").start();\n const token = await passwordLogin(spinner, baseUrl, answers.email, answers.password);\n await finishWithToken(spinner, baseUrl, token, opts.org);\n });\n", "export const DEFAULT_PORT = 3000;\n\nexport const SUPPORTED_RUNTIMES = [\n \"node-20\",\n \"node-22\",\n \"python-3.11\",\n \"python-3.12\",\n \"bun-1\",\n \"deno-2\",\n] as const;\n\nexport type SupportedRuntime = (typeof SUPPORTED_RUNTIMES)[number];\n\nexport const DEPLOYMENT_STATUSES = [\n \"building\",\n \"deploying\",\n \"running\",\n \"failed\",\n // A task that was live and then died on its own -- OOM, panic, ECS\n // replacement. The reconcile pass sets it, so anything enumerating statuses\n // has to know about it.\n \"crashed\",\n \"stopped\",\n] as const;\n\nexport const ENVIRONMENTS = [\"preview\", \"production\"] as const;\n\nexport const ROLES = [\"admin\", \"member\"] as const;\n\nexport const DEFAULT_API_URL = \"https://api.videan.xyz/v1\";\n\n/** The zone every app's hostname hangs off. */\nexport const APP_DOMAIN = process.env.CLOUD_APP_DOMAIN || \"videan.xyz\";\n\n/**\n * The public URL of an app, from its slug.\n *\n * One definition, because it was previously written out by hand in three places\n * and one of them omitted the scheme -- so a deployment's `url` came back as a\n * bare `slug.videan.xyz` that no terminal, browser or `fetch` would follow,\n * while the app resource reported the same app as `https://slug.videan.xyz`.\n * HTTP redirects to HTTPS anyway, so the scheme is not optional in practice.\n */\nexport function appUrl(slug: string): string {\n return `https://${slug}.${APP_DOMAIN}`;\n}\n\n/**\n * Prefix for a single deployment's own hostname.\n *\n * Every deployment is reachable before it is promoted, which is what lets an\n * agent verify its own work without production riding on the answer. The label is\n * a hash of the deployment id rather than the id itself: nanoid's alphabet\n * contains `_` and uppercase, neither of which is legal in a DNS label, and\n * lowercasing or substituting would map two distinct deployments onto one\n * hostname -- serving whichever the router looked up first.\n *\n * The prefix keeps the namespace from colliding with app slugs: `*.videan.xyz`\n * is one flat namespace, and an app legitimately slugged the same as some\n * deployment's hash would otherwise steal its traffic.\n */\nexport const PREVIEW_LABEL_PREFIX = \"d-\";\n\nexport function previewUrl(label: string): string {\n return `https://${PREVIEW_LABEL_PREFIX}${label}.${APP_DOMAIN}`;\n}\n\n/** True for a hostname label that belongs to the preview namespace. */\nexport function isPreviewLabel(label: string): boolean {\n return label.startsWith(PREVIEW_LABEL_PREFIX);\n}\n\nexport const CONFIG_FILE_NAME = \"cloud.yaml\";\n\nexport const IGNORE_FILE_NAME = \".cloudignore\";\n\nexport const MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; // 50MB\n\nexport const MAX_FILES_PER_PUSH = 10_000;\n", "/**\n * Runtime detection and Dockerfile generation.\n *\n * This is the single source of truth for \"what kind of app is this and how do we\n * containerize it\". The control API generates build contexts from it and the\n * builder service reports runtime from it; two implementations would eventually\n * disagree about the same snapshot.\n */\n\nexport type Runtime = \"custom\" | \"nextjs\" | \"nodejs\" | \"python\" | \"go\";\n\n/**\n * Every app is served on this port.\n *\n * The edge router resolves a task to `ip:APP_PORT`, so detection cannot pick a\n * per-runtime port -- a Python app on 8000 would simply be unroutable. Generated\n * Dockerfiles set PORT=3000 and EXPOSE 3000; a project supplying its own\n * Dockerfile must listen here too.\n */\nexport const APP_PORT = 3000;\n\nexport const RUNTIMES: Runtime[] = [\"custom\", \"nextjs\", \"nodejs\", \"python\", \"go\"];\n\n/**\n * Narrows a string that crossed a boundary as `string` -- the builder reports the\n * detected runtime as plain text, and a caller keying a table off it needs to know\n * the value is one of ours rather than assume it.\n */\nexport function isRuntime(value: string): value is Runtime {\n return (RUNTIMES as string[]).includes(value);\n}\n\nexport interface DetectionResult {\n runtime: Runtime;\n port: number;\n /** True when the project shipped its own Dockerfile and we generate nothing. */\n hasDockerfile: boolean;\n /** Why this runtime was chosen -- surfaced in build logs. */\n reason: string;\n}\n\nconst NEXT_CONFIG = /^next\\.config\\.(js|cjs|mjs|ts|mts)$/;\n\n/**\n * The AWS Lambda Web Adapter, injected into every generated image.\n *\n * It is what lets one contract (\"serve HTTP on port 3000\") run on Lambda at all:\n * Lambda speaks an invoke API, not HTTP, and the adapter translates between them\n * in front of an ordinary web server. The app itself needs no Lambda awareness,\n * which is the whole point -- the same code and the same image serve both\n * execution modes.\n *\n * Injected **unconditionally**, including for apps that will only ever run on\n * Fargate. `/opt/extensions` binaries are started by the Lambda runtime and by\n * nothing else, so under Fargate this is an unused ~4 MB file: inert, not\n * conditional. That is deliberate and load-bearing -- it means switching an app\n * between `container` and `serverless` is a pointer move over the image that\n * already exists, not a rebuild. A mode-conditional Dockerfile would make the\n * image mode-specific and turn every mode change into a full build.\n *\n * Pinned to an exact adapter version for the same reason every base image here is:\n * `:latest` would silently change what a rebuild of an unchanged snapshot produces.\n *\n * `AWS_LWA_READINESS_CHECK_PATH` is deliberately unset -- the adapter's default\n * is a TCP connect to the port, which is exactly the platform's contract. A path\n * would require every app to serve some agreed health route, which is a second\n * contract.\n */\nconst LAMBDA_ADAPTER = `COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter\nENV AWS_LWA_PORT=${APP_PORT}`;\n\n/**\n * Amazon's RDS CA bundle, installed into every generated Node image.\n *\n * The injected `DATABASE_URL` carries `sslmode=require`, which under libpq means\n * \"encrypt, do not verify\" -- so psycopg and pgx connect happily. `pg` >= 8.16\n * deliberately diverges and treats `require` as an alias for `verify-full`, and\n * the RDS roots are in no distribution's trust store, so **every** Node app that\n * opened its managed database died on `unable to get local issuer certificate`\n * after a successful build and a successful promotion. Reported as a crash, on\n * the one code path a tenant cannot work around from their own repo.\n *\n * Fixed by supplying the CA rather than by weakening the mode: `sslmode=no-verify`\n * would work for `pg` and is not a libpq mode at all, so it would break the two\n * runtimes that currently work, and one URL is injected for all of them. With the\n * bundle present, verify-full genuinely verifies.\n *\n * `ADD` fails the build if the fetch fails, which is the right direction: a\n * silently absent CA is the bug being fixed here.\n *\n * The `chmod` is what makes it work under Lambda as well as Fargate. Docker gives\n * a file fetched by `ADD <url>` mode 600 owned by root, and the generated node\n * images declare no `USER`, so under Fargate the app runs as root and reads it\n * fine -- but Lambda executes a container image as its own unprivileged sandbox\n * user, which cannot open a root-only file. Node's response to an unreadable\n * `NODE_EXTRA_CA_CERTS` is to warn and carry on with the default store, so the\n * symptom is not a startup failure but the original bug returning verbatim:\n * `unable to get local issuer certificate`, from an image whose environment\n * plainly names the bundle. Written as a `RUN` rather than `ADD --chmod=644`\n * because that flag needs BuildKit and CodeBuild builds these with the classic\n * builder.\n */\nconst RDS_CA = `# Lets a managed-Postgres connection be verified, not merely encrypted. See\n# packages/shared/src/detect.ts -- without this, \\`pg\\` cannot connect at all.\nADD https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem /etc/ssl/certs/rds-ca.pem\nRUN chmod 644 /etc/ssl/certs/rds-ca.pem\nENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/rds-ca.pem`;\n\n/**\n * Detects the runtime from a snapshot's file list.\n *\n * Paths are relative and may carry a leading \"./\". Order matters: a project can\n * satisfy several checks at once (a Next.js app has a package.json too), so the\n * most specific signal has to win.\n */\nexport function detectRuntime(files: string[]): DetectionResult {\n const set = new Set(files.map((f) => f.replace(/^\\.\\//, \"\")));\n\n // A hand-written Dockerfile always wins -- it is an explicit instruction.\n if (set.has(\"Dockerfile\") || set.has(\"dockerfile\")) {\n return {\n runtime: \"custom\",\n port: APP_PORT,\n hasDockerfile: true,\n reason: \"project provided a Dockerfile\",\n };\n }\n\n const hasPackageJson = set.has(\"package.json\");\n const hasNextConfig = [...set].some((f) => NEXT_CONFIG.test(f));\n const hasRequirements = set.has(\"requirements.txt\");\n const hasPyproject = set.has(\"pyproject.toml\");\n const hasGoMod = set.has(\"go.mod\");\n\n if (hasPackageJson && hasNextConfig) {\n return {\n runtime: \"nextjs\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found package.json and a next.config file\",\n };\n }\n\n if (hasPackageJson) {\n return {\n runtime: \"nodejs\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found package.json\",\n };\n }\n\n if (hasRequirements || hasPyproject) {\n return {\n runtime: \"python\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: hasRequirements ? \"found requirements.txt\" : \"found pyproject.toml\",\n };\n }\n\n if (hasGoMod) {\n return {\n runtime: \"go\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found go.mod\",\n };\n }\n\n throw new Error(\n \"Could not detect a runtime. Include a package.json, requirements.txt, \" +\n \"pyproject.toml, go.mod, or your own Dockerfile.\",\n );\n}\n\n/** The Python entry point to run, in order of preference. */\nfunction pythonEntry(files: string[]): string | null {\n const set = new Set(files.map((f) => f.replace(/^\\.\\//, \"\")));\n for (const candidate of [\"main.py\", \"app.py\", \"server.py\", \"manage.py\"]) {\n if (set.has(candidate)) return candidate;\n }\n return null;\n}\n\n/**\n * Generates the Dockerfile for a detected runtime.\n *\n * Throws for `custom`: that project has its own Dockerfile and overwriting it\n * would silently discard the author's build.\n */\nexport function generateDockerfile(files: string[], detection?: DetectionResult): string {\n const detected = detection ?? detectRuntime(files);\n\n if (detected.runtime === \"custom\") {\n throw new Error(\"Project supplies its own Dockerfile; nothing to generate\");\n }\n\n switch (detected.runtime) {\n case \"nextjs\":\n return `FROM public.ecr.aws/docker/library/node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm install\nCOPY . .\nRUN npm run build\n${RDS_CA}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV HOSTNAME=0.0.0.0\nEXPOSE ${APP_PORT}\nCMD [\"npm\", \"start\"]\n`;\n\n case \"nodejs\":\n return `FROM public.ecr.aws/docker/library/node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\n# npm ci needs a lockfile; fall back to install when there is none.\nRUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi\nCOPY . .\n${RDS_CA}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV HOST=0.0.0.0\nEXPOSE ${APP_PORT}\nCMD [\"npm\", \"start\"]\n`;\n\n case \"python\": {\n const entry = pythonEntry(files);\n // No recognisable entry point: install deps and let the image fail loudly\n // at start rather than guessing a module name.\n const cmd = entry\n ? `CMD [\"python\", \"${entry}\"]`\n : `CMD [\"sh\", \"-c\", \"echo 'No entry point found (expected main.py, app.py, server.py or manage.py)' >&2; exit 1\"]`;\n const install = `RUN if [ -f requirements.txt ]; then pip install --no-cache-dir -r requirements.txt; else pip install --no-cache-dir .; fi`;\n return `FROM public.ecr.aws/docker/library/python:3.12-slim\nWORKDIR /app\nCOPY . .\n${install}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV PYTHONUNBUFFERED=1\nEXPOSE ${APP_PORT}\n${cmd}\n`;\n }\n\n case \"go\":\n return `FROM public.ecr.aws/docker/library/golang:1.22-alpine AS builder\nWORKDIR /build\nCOPY go.* ./\nRUN go mod download\nCOPY . .\nRUN CGO_ENABLED=0 go build -o app .\n\nFROM public.ecr.aws/docker/library/alpine:3.20\nWORKDIR /app\nCOPY --from=builder /build/app .\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nEXPOSE ${APP_PORT}\nCMD [\"./app\"]\n`;\n }\n}\n\n/**\n * Derives an app's URL slug from its name.\n *\n * The slug becomes a DNS label (`<slug>.videan.xyz`), so it must be lowercase\n * alphanumeric with inner hyphens and at most 63 characters.\n */\nexport function slugify(name: string): string {\n return name\n .toLowerCase()\n .normalize(\"NFKD\")\n // Strip combining marks so \"Caf\u00E9\" becomes \"cafe\", not \"caf\".\n .replace(/[\u0300-\u036F]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 63)\n // Slicing can leave a trailing hyphen, which is not a valid DNS label.\n .replace(/-+$/g, \"\");\n}\n", "/**\n * Fargate task sizing.\n *\n * Every app used to run at 256 CPU / 512 MB, hardcoded in `ecs-app.ts`. That is\n * below the working set of a stock Next.js server, so those apps were killed by\n * the OOM killer -- which surfaces as a container that exits without a message,\n * indistinguishable from a crash in the app's own code. Sizing is therefore not\n * a tuning knob; at the old default some runtimes could not work at all.\n *\n * Fargate accepts only specific (cpu, memory) pairs and rejects everything else\n * at RegisterTaskDefinition time. Validating here rather than there means the\n * caller is told which values are legal instead of reading an AWS error, and the\n * CLI can reject a bad `--memory` before a build starts.\n */\n\nimport type { Runtime } from \"./detect.js\";\n\n/**\n * Legal memory values (MiB) for each CPU unit, from the Fargate task-size table.\n *\n * Expressed as a range plus a step rather than a literal list because the larger\n * tiers have hundreds of legal values; a list would be unreadable and would drift.\n */\nconst FARGATE_SIZES: Record<number, { min: number; max: number; step: number }> = {\n 256: { min: 512, max: 2048, step: 512 },\n 512: { min: 1024, max: 4096, step: 1024 },\n 1024: { min: 2048, max: 8192, step: 1024 },\n 2048: { min: 4096, max: 16384, step: 1024 },\n 4096: { min: 8192, max: 30720, step: 1024 },\n 8192: { min: 16384, max: 61440, step: 4096 },\n 16384: { min: 32768, max: 122880, step: 8192 },\n};\n\nexport const FARGATE_CPUS = Object.keys(FARGATE_SIZES)\n .map(Number)\n .sort((a, b) => a - b);\n\n/**\n * 256 CPU is a special case: its legal memory values are 512, 1024 and 2048 --\n * it skips 1536, so the uniform step above would admit a value Fargate rejects.\n */\nconst CPU_256_MEMORY = [512, 1024, 2048];\n\nexport interface SizeValidation {\n ok: boolean;\n /** Why the pair is illegal, naming the legal alternatives. */\n error?: string;\n /** The legal memory values for the given CPU, for building an error message. */\n allowed?: number[];\n}\n\nfunction legalMemory(cpu: number): number[] {\n if (cpu === 256) return CPU_256_MEMORY;\n const spec = FARGATE_SIZES[cpu];\n if (!spec) return [];\n const values: number[] = [];\n for (let m = spec.min; m <= spec.max; m += spec.step) values.push(m);\n return values;\n}\n\n/**\n * Checks a (cpu, memory) pair against the Fargate table.\n *\n * Deliberately strict rather than snapping to the nearest legal value: silently\n * giving an app 2 GB when its owner asked for 1.5 GB means the bill and the\n * behaviour do not match what was requested, and a size that is quietly changed\n * is a size nobody can reason about when the app OOMs anyway.\n */\nexport function validateSize(cpu: number, memory: number): SizeValidation {\n if (!FARGATE_SIZES[cpu]) {\n return {\n ok: false,\n error: `cpu must be one of ${FARGATE_CPUS.join(\", \")} (got ${cpu})`,\n };\n }\n const allowed = legalMemory(cpu);\n if (!allowed.includes(memory)) {\n // Long lists are unhelpful in an error message; show the shape instead.\n const shown =\n allowed.length <= 6\n ? allowed.join(\", \")\n : `${allowed[0]}\u2013${allowed[allowed.length - 1]} in steps of ${allowed[1] - allowed[0]}`;\n return {\n ok: false,\n error: `memory ${memory} is not valid for cpu ${cpu}; allowed: ${shown}`,\n allowed,\n };\n }\n return { ok: true };\n}\n\nexport interface TaskSize {\n cpu: number;\n memory: number;\n}\n\n/**\n * The size an app gets when nobody chose one.\n *\n * Picked per runtime because the failure is runtime-specific: `next start` holds\n * a compiled server plus its module graph and does not fit in 512 MB, while a\n * Go binary is comfortable in it. Defaulting everything to the larger size would\n * quadruple the bill for the runtimes that do not need it; defaulting everything\n * to the smaller one is the bug this replaces.\n */\nexport function defaultSizeFor(runtime: Runtime): TaskSize {\n switch (runtime) {\n case \"nextjs\":\n return { cpu: 512, memory: 1024 };\n case \"nodejs\":\n case \"python\":\n // Comfortable for a framework plus dependencies, and the smallest legal\n // pair above the old default.\n return { cpu: 256, memory: 1024 };\n case \"go\":\n return { cpu: 256, memory: 512 };\n case \"custom\":\n // Unknown contents, so assume it is doing something real.\n return { cpu: 512, memory: 1024 };\n }\n}\n\n/**\n * How many tasks a size change or a scale-out may run at once.\n *\n * A bound exists because `instances` is caller-supplied and reaches RunTask\n * directly: a typo of 100 would launch a hundred Fargate tasks and bill for them\n * before anyone noticed. RunTask itself caps a single call at 10.\n */\nexport const MAX_INSTANCES = 10;\n", "/**\n * What a plan allows and what usage costs.\n *\n * Kept here rather than in the control API because three consumers need the same\n * numbers and must not disagree about them: the API enforces the limits, the\n * usage collector prices the metrics it records, and the CLI prints a bill. A\n * rate duplicated in two of those places is a bill that does not match the\n * invoice.\n *\n * Two things this deliberately is *not*:\n *\n * - **A payment integration.** Nothing here charges anyone. It computes what an\n * org owes; collecting it needs a payment processor and a human to accept its\n * terms, the same way SES production access does.\n * - **The authority on AWS cost.** These are the platform's *prices*. The real\n * cost of a Fargate task is in Cost Explorer, and the two will not match --\n * that difference is the margin, and pretending otherwise would make every\n * price change look like a metering bug.\n */\n\n/**\n * Metrics recorded in `usage_records`.\n *\n * `vcpu_seconds` and `gb_seconds` are what Fargate actually charges for, so they\n * are what a compute line is priced from; `task_seconds` is recorded alongside\n * them because it is the number a human recognises (\"this ran for 20 hours\") and\n * cannot be recovered from the other two without knowing the task size.\n *\n * `storage_mb`, not bytes: `usage_records.quantity` is an `integer`, and bytes\n * overflows int32 at 2.1 GB -- silently, wrapping negative, which would read as a\n * credit.\n *\n * `lambda_gb_seconds` and `lambda_invocations` are the serverless compute line,\n * deliberately *not* folded into `gb_seconds`. They price differently, they come\n * from a different source (CloudWatch's own measurement rather than arithmetic\n * over a deployment row), and an app that switched modes mid-month has to produce\n * an invoice a person can audit -- two lines that each say where they came from,\n * not one line that silently mixes two meters.\n */\nexport type UsageMetric =\n | \"task_seconds\"\n | \"vcpu_seconds\"\n | \"gb_seconds\"\n | \"requests\"\n | \"build_seconds\"\n | \"storage_mb\"\n | \"emails_sent\"\n | \"db_days\"\n | \"lambda_gb_seconds\"\n | \"lambda_invocations\";\n\nexport const USAGE_METRICS: UsageMetric[] = [\n \"task_seconds\",\n \"vcpu_seconds\",\n \"gb_seconds\",\n \"requests\",\n \"build_seconds\",\n \"storage_mb\",\n \"emails_sent\",\n \"db_days\",\n \"lambda_gb_seconds\",\n \"lambda_invocations\",\n];\n\n/**\n * Metrics that are a level rather than a total.\n *\n * A day's `storage_mb` row is the last sample taken that day, not a sum of\n * samples -- summing a level double-counts every byte for every time it was\n * observed. Billing a level means averaging the samples across the period, which\n * is why the two kinds cannot be aggregated by the same code path.\n */\nexport const GAUGE_METRICS: UsageMetric[] = [\"storage_mb\"];\n\nexport function isGauge(metric: string): boolean {\n return (GAUGE_METRICS as string[]).includes(metric);\n}\n\n/**\n * Price per unit, in cents. Fractional on purpose: a request costs a fifth of a\n * millionth of a cent and rounding each one to a cent would bill $10,000 for a\n * million requests.\n *\n * `task_seconds` is priced at zero because compute is already billed through\n * `vcpu_seconds` and `gb_seconds`. It is in the table rather than absent from it\n * so that a new metric added without a price is a visible zero here instead of an\n * `undefined` multiplied into `NaN` further down.\n */\nexport const RATES: Record<UsageMetric, number> = {\n task_seconds: 0,\n // Fargate on-demand in us-east-1 is ~$0.04048/vCPU-hour and ~$0.004445/GB-hour.\n vcpu_seconds: 0.002,\n gb_seconds: 0.00025,\n // $0.20 per million requests.\n requests: 0.00002,\n // ~$0.012 per build-minute.\n build_seconds: 0.02,\n // $0.05 per GB-month, charged per MB-month.\n storage_mb: 0.005,\n // $0.50 per thousand.\n emails_sent: 0.05,\n // ~$3/month for a managed schema, charged per day it exists.\n db_days: 10,\n // Lambda list price is $0.0000166667 per GB-second; this carries roughly the\n // same markup over list as the Fargate rates above, so the two compute lines\n // are comparable and a mode switch does not look like a price change.\n lambda_gb_seconds: 0.0033,\n // Lambda's own $0.20 per million invocations, passed through at list. The\n // platform's markup on a request is already collected once by the `requests`\n // line at the edge, and one visit is one event -- marking it up on both meters\n // would charge twice for the same request.\n lambda_invocations: 0.00002,\n};\n\n/** Human unit for each metric, for a bill a person has to read. */\nexport const METRIC_LABELS: Record<UsageMetric, string> = {\n task_seconds: \"Task-seconds served\",\n vcpu_seconds: \"vCPU-seconds\",\n gb_seconds: \"GB-seconds\",\n requests: \"Requests\",\n build_seconds: \"Build seconds\",\n storage_mb: \"Object storage (MB-month)\",\n emails_sent: \"Emails sent\",\n db_days: \"Managed Postgres (app-days)\",\n lambda_gb_seconds: \"Serverless GB-seconds\",\n lambda_invocations: \"Serverless invocations\",\n};\n\nexport interface Plan {\n id: string;\n name: string;\n /** Monthly subscription, in cents, before usage. */\n baseCents: number;\n /**\n * Usage covered by the base fee, in cents. An org that stays under this pays\n * exactly `baseCents` -- so the free plan is a real free tier rather than a\n * $0.03 invoice for a weekend project.\n */\n includedUsageCents: number;\n /** Apps that are not soft-deleted. */\n maxApps: number;\n maxInstancesPerApp: number;\n /** Ceilings on Fargate task size, which is the one knob that scales the bill. */\n maxCpu: number;\n maxMemory: number;\n /** Object-storage quota, enforced on upload. */\n maxStorageMb: number;\n postgres: boolean;\n customDomains: boolean;\n}\n\n/**\n * The plans.\n *\n * The limits that exist are the ones that bound *spend*: app count, instance\n * count and task size. There is deliberately no limit on requests, builds or\n * deploys -- an agent-built app that gets popular should get a bill, not an\n * outage, and a platform whose selling point is \"push and it is live\" cannot\n * answer a deploy with \"not on your plan\".\n */\nexport const PLANS: Record<string, Plan> = {\n free: {\n id: \"free\",\n name: \"Free\",\n baseCents: 0,\n includedUsageCents: 300,\n maxApps: 3,\n maxInstancesPerApp: 1,\n // One app at this size, running all month, is inside the included usage.\n maxCpu: 512,\n maxMemory: 1024,\n maxStorageMb: 1024,\n postgres: true,\n customDomains: false,\n },\n pro: {\n id: \"pro\",\n name: \"Pro\",\n baseCents: 2000,\n includedUsageCents: 2000,\n maxApps: 50,\n maxInstancesPerApp: 4,\n maxCpu: 4096,\n maxMemory: 8192,\n maxStorageMb: 100 * 1024,\n postgres: true,\n customDomains: true,\n },\n scale: {\n id: \"scale\",\n name: \"Scale\",\n baseCents: 20000,\n includedUsageCents: 20000,\n maxApps: 500,\n maxInstancesPerApp: 10,\n maxCpu: 16384,\n maxMemory: 122880,\n maxStorageMb: 1024 * 1024,\n postgres: true,\n customDomains: true,\n },\n};\n\n/**\n * The plan for a stored `orgs.plan` value.\n *\n * Falls back to free rather than throwing: the column is free text and an\n * unrecognised value must not take an org's whole control surface down with a\n * 500. Free is the safe direction -- its limits are the tightest, so a typo\n * cannot silently grant a larger allowance than anyone agreed to.\n */\nexport function planFor(plan: string | null | undefined): Plan {\n return PLANS[plan || \"free\"] ?? PLANS.free;\n}\n\nexport interface UsageLine {\n metric: UsageMetric | string;\n label: string;\n quantity: number;\n unitPriceCents: number;\n /** Rounded to whole cents only at the line, never per unit. */\n cents: number;\n}\n\nexport interface Invoice {\n plan: string;\n periodStart: string;\n periodEnd: string;\n lines: UsageLine[];\n /** Everything the usage lines add up to, before the included allowance. */\n usageCents: number;\n baseCents: number;\n includedUsageCents: number;\n /** Usage beyond the allowance. Zero, never negative: credit does not roll over. */\n overageCents: number;\n totalCents: number;\n}\n\n/**\n * Prices a period's usage.\n *\n * `quantities` is metric \u2192 quantity, already aggregated over the period (sums for\n * counters, averages for gauges -- see `GAUGE_METRICS`, and note that this\n * function cannot tell which it was given, so aggregating wrongly produces a\n * plausible bill rather than an error).\n */\nexport function priceUsage(\n quantities: Record<string, number>,\n plan: Plan,\n period: { start: Date; end: Date },\n): Invoice {\n const lines: UsageLine[] = [];\n for (const metric of USAGE_METRICS) {\n const quantity = quantities[metric] ?? 0;\n const rate = RATES[metric];\n // A zero-priced metric is still reported when there is something to report:\n // task-seconds is how an owner checks that the compute line is plausible.\n if (quantity === 0) continue;\n lines.push({\n metric,\n label: METRIC_LABELS[metric],\n quantity: Math.round(quantity * 100) / 100,\n unitPriceCents: rate,\n cents: Math.round(quantity * rate),\n });\n }\n // Metrics recorded by a newer collector than this pricing table knows about.\n // Listed at zero rather than dropped: an unpriced metric that vanishes from the\n // bill is indistinguishable from one that was never collected.\n for (const metric of Object.keys(quantities)) {\n if ((USAGE_METRICS as string[]).includes(metric)) continue;\n if (!quantities[metric]) continue;\n lines.push({\n metric,\n label: metric,\n quantity: quantities[metric],\n unitPriceCents: 0,\n cents: 0,\n });\n }\n\n const usageCents = lines.reduce((acc, l) => acc + l.cents, 0);\n const overageCents = Math.max(0, usageCents - plan.includedUsageCents);\n return {\n plan: plan.id,\n periodStart: period.start.toISOString(),\n periodEnd: period.end.toISOString(),\n lines,\n usageCents,\n baseCents: plan.baseCents,\n includedUsageCents: plan.includedUsageCents,\n overageCents,\n totalCents: plan.baseCents + overageCents,\n };\n}\n\n/** `1234` \u2192 `\"$12.34\"`. For output a person reads, not for arithmetic. */\nexport function formatCents(cents: number): string {\n const sign = cents < 0 ? \"-\" : \"\";\n const abs = Math.abs(Math.round(cents));\n return `${sign}$${Math.floor(abs / 100)}.${String(abs % 100).padStart(2, \"0\")}`;\n}\n\nexport interface LimitCheck {\n ok: boolean;\n /** Names the limit and the plan, so the error says what to do about it. */\n error?: string;\n code?: string;\n}\n\nexport function allowed(): LimitCheck {\n return { ok: true };\n}\n\nfunction denied(code: string, error: string): LimitCheck {\n return { ok: false, error, code };\n}\n\n/** Whether one more app is within the plan. `current` excludes deleted apps. */\nexport function checkAppCount(plan: Plan, current: number): LimitCheck {\n if (current < plan.maxApps) return allowed();\n return denied(\n \"PLAN_LIMIT_APPS\",\n `The ${plan.name} plan allows ${plan.maxApps} app${plan.maxApps === 1 ? \"\" : \"s\"} ` +\n `and this org has ${current}. Delete one or upgrade the plan.`,\n );\n}\n\n/**\n * Whether a requested task shape is within the plan.\n *\n * Checked as a triple rather than three separate limits because they multiply:\n * the thing being bounded is the bill, and 4 instances of a small task costs the\n * same as one large one.\n */\nexport function checkTaskShape(\n plan: Plan,\n shape: { cpu?: number | null; memory?: number | null; instances?: number | null },\n): LimitCheck {\n if (shape.instances != null && shape.instances > plan.maxInstancesPerApp) {\n return denied(\n \"PLAN_LIMIT_INSTANCES\",\n `The ${plan.name} plan allows ${plan.maxInstancesPerApp} instance` +\n `${plan.maxInstancesPerApp === 1 ? \"\" : \"s\"} per app (asked for ${shape.instances}).`,\n );\n }\n if (shape.cpu != null && shape.cpu > plan.maxCpu) {\n return denied(\n \"PLAN_LIMIT_CPU\",\n `The ${plan.name} plan allows up to ${plan.maxCpu} CPU units per task (asked for ${shape.cpu}).`,\n );\n }\n if (shape.memory != null && shape.memory > plan.maxMemory) {\n return denied(\n \"PLAN_LIMIT_MEMORY\",\n `The ${plan.name} plan allows up to ${plan.maxMemory} MB per task (asked for ${shape.memory}).`,\n );\n }\n return allowed();\n}\n\n/** Whether a custom domain is within the plan. */\nexport function checkCustomDomain(plan: Plan): LimitCheck {\n if (plan.customDomains) return allowed();\n return denied(\n \"PLAN_LIMIT_DOMAINS\",\n `Custom domains are not included in the ${plan.name} plan. Every app keeps its ` +\n `platform hostname, which needs no configuration.`,\n );\n}\n", "import type {\n App,\n Snapshot,\n Deployment,\n Secret,\n Organization,\n OrgMember,\n LogEntry,\n LogPage,\n AppMetrics,\n CreateAppRequest,\n ApiResponse,\n Environment,\n AppHealth,\n BuildLogs,\n ApiKey,\n ApiKeyWithSecret,\n Job,\n OrgUsage,\n AppUsage,\n PlanInfo,\n AppMode,\n Visibility,\n} from \"@cloud/shared\";\nimport { DEFAULT_API_URL } from \"@cloud/shared\";\n\n/**\n * Only all-lowercase snake_case, which is what the API emits for its own fields.\n *\n * Deliberately narrow. Data the platform carries on behalf of a user also\n * travels through here -- secret names like `DATABASE_URL`, file paths like\n * `app/page.js` -- and rewriting one of those would corrupt it. Uppercase, dots\n * and slashes all fail this test, so those pass through untouched.\n */\nconst SNAKE_KEY = /^[a-z0-9]+(_[a-z0-9]+)+$/;\n\n/**\n * Fields whose *contents* belong to the caller, not to the API.\n *\n * A job's payload is a blob the app wrote and the app will read back; renaming\n * `order_id` inside it to `orderId` would corrupt the app's own data on its way\n * through the SDK. Same reasoning as the narrow key pattern above -- the key\n * itself is still converted, only what hangs off it is left alone.\n */\nconst OPAQUE_KEYS = new Set([\"payload\", \"files\", \"metadata\"]);\n\n/**\n * Recursively rewrites snake_case keys to camelCase.\n *\n * The API's wire format is snake_case (`created_at`, `key_prefix`); the SDK's\n * types are camelCase. Nothing used to bridge the two -- `response.json()` was\n * simply cast to the typed shape -- so every such field was `undefined` at\n * runtime while the compiler insisted it was a string. That is the worst\n * possible failure mode for a typed client: `deployment.createdAt` produced\n * `NaN` elapsed times and `key.keyPrefix` printed \"undefined\u2026\", with no error\n * anywhere. The conversion belongs here rather than in the API, which other\n * consumers already read as snake_case.\n */\nexport function camelizeDeep(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(camelizeDeep);\n if (value === null || typeof value !== \"object\") return value;\n // Dates survive JSON as strings, so anything object-shaped here is a plain\n // object from JSON.parse.\n const out: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value as Record<string, unknown>)) {\n const camel = SNAKE_KEY.test(key)\n ? key.replace(/_([a-z0-9])/g, (_, c: string) => c.toUpperCase())\n : key;\n out[camel] = OPAQUE_KEYS.has(camel) ? v : camelizeDeep(v);\n }\n return out;\n}\n\n/**\n * The type-level counterpart of `camelizeDeep`.\n *\n * Some shared types describe the *wire* shape and keep its snake_case (`LogPage`,\n * `BuildLogs`), because that is what a non-SDK consumer reading the API sees. The\n * SDK rewrites those keys at runtime, so returning the wire type from an SDK\n * method would reintroduce exactly the bug `camelizeDeep` was written to fix: a\n * field the compiler swears is a string and that is `undefined` at runtime. This\n * makes the declared type follow the conversion instead of contradicting it.\n */\ntype CamelKey<K extends string> = K extends `${infer A}_${infer B}`\n ? `${A}${Capitalize<CamelKey<B>>}`\n : K;\n\nexport type Camelize<T> = T extends (infer U)[]\n ? Camelize<U>[]\n : T extends object\n ? { [K in keyof T as K extends string ? CamelKey<K> : K]: Camelize<T[K]> }\n : T;\n\nexport class CloudError extends Error {\n public readonly code: string;\n public readonly status: number;\n public readonly details?: Record<string, unknown>;\n\n constructor(message: string, code: string, status: number, details?: Record<string, unknown>) {\n super(message);\n this.name = \"CloudError\";\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n\nexport interface CloudClientOptions {\n apiKey: string;\n baseUrl?: string;\n}\n\nexport class CloudClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n\n public readonly apps: AppsNamespace;\n public readonly secrets: SecretsNamespace;\n public readonly orgs: OrgsNamespace;\n public readonly apiKeys: ApiKeysNamespace;\n public readonly storage: StorageNamespace;\n public readonly email: EmailNamespace;\n public readonly jobs: JobsNamespace;\n\n constructor(options: CloudClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_API_URL).replace(/\\/$/, \"\");\n this.apps = new AppsNamespace(this);\n this.secrets = new SecretsNamespace(this);\n this.orgs = new OrgsNamespace(this);\n this.apiKeys = new ApiKeysNamespace(this);\n this.storage = new StorageNamespace(this);\n this.email = new EmailNamespace(this);\n this.jobs = new JobsNamespace(this);\n }\n\n /**\n * The plan catalogue. Unauthenticated on the API, so this works with any key --\n * including a placeholder one -- and can be called before an account exists to\n * show what an upgrade would allow.\n */\n async plans(): Promise<PlanInfo[]> {\n const res = await this.request<ApiResponse<PlanInfo[]>>(\"GET\", \"/plans\");\n return res.data;\n }\n\n async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const headers: Record<string, string> = {\n \"Authorization\": `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n\n // `fetch` reports every transport failure as the same bare `TypeError: fetch\n // failed`, with the cause one level down and the URL nowhere in it. A caller\n // seeing that cannot tell a DNS mistake from a reset connection from an API\n // that is down, which is the same class of unhelpfulness as the old\n // \"check your credentials\" for a 404. Status 0 says the request never got an\n // answer, so a retry is reasonable -- unlike a 4xx.\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n } catch (e: any) {\n const cause = e?.cause?.code || e?.cause?.message || e?.message || \"unknown error\";\n throw new CloudError(\n `Could not reach ${url} (${cause}).`,\n \"network_error\",\n 0,\n );\n }\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({}));\n const err = (errorBody as { error?: { code?: string; message?: string; details?: Record<string, unknown> } }).error;\n throw new CloudError(\n err?.message ?? `Request failed with status ${response.status}`,\n err?.code ?? \"unknown_error\",\n response.status,\n err?.details,\n );\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return camelizeDeep(await response.json()) as T;\n }\n}\n\nclass AppsNamespace {\n constructor(private client: CloudClient) {}\n\n async create(opts: {\n name: string;\n orgId: string;\n postgres?: boolean;\n /** Gives the app an object-storage prefix; see `client.storage`. */\n storage?: boolean;\n /** Lets the app send mail through the platform; see `client.email`. */\n email?: boolean;\n /**\n * Who may reach the running app: \"private\" | \"org\" | \"authenticated\" |\n * \"public\", narrowest first. Defaults to \"org\". Prefer this over\n * `authRequired`, which cannot express the difference between \"only the\n * people I invite\" and \"anyone in my org\".\n */\n visibility?: Visibility;\n /** @deprecated Use `visibility`. false is equivalent to \"public\". */\n authRequired?: boolean;\n /**\n * Groups the apps of one logical system under a shared label -- set the\n * same value on a split frontend and backend so the console shows them as\n * one stack. Slug-shaped: lowercase letters, digits and hyphens.\n */\n stack?: string;\n /**\n * How the app runs: \"container\" (a Fargate task, always on) or\n * \"serverless\" (a Lambda function that scales to zero and bills only for\n * requests it serves). Defaults to \"container\".\n *\n * Pick \"serverless\" for request/response HTTP -- a dashboard, an API, a\n * webhook receiver -- and \"container\" when the app needs cron, background\n * jobs, WebSockets, a custom Dockerfile, work longer than a minute, or\n * in-process state that must survive between requests. Either way the\n * contract is the same: listen on port 3000.\n */\n mode?: AppMode;\n }): Promise<App> {\n const body: CreateAppRequest = {\n name: opts.name,\n org_id: opts.orgId,\n postgres_enabled: opts.postgres,\n storage_enabled: opts.storage,\n email_enabled: opts.email,\n auth_required: opts.authRequired,\n visibility: opts.visibility,\n stack: opts.stack,\n mode: opts.mode,\n };\n const res = await this.client.request<ApiResponse<App>>(\"POST\", \"/apps\", body);\n return res.data;\n }\n\n /**\n * Changes an app's settings.\n *\n * `restartRequired` is true when the change only takes effect on a new\n * container -- size, scale and postgres. The API reports it rather than\n * restarting on its own: a restart is a brief interruption, and doing it as a\n * side effect of a settings write is not the caller's decision to discover\n * afterwards. Follow with `restart()` when it comes back true.\n */\n async update(\n appId: string,\n changes: {\n name?: string;\n /**\n * Who may reach the running app. Admin-level: an editor may deploy the\n * app but not change who can open it.\n *\n * Note that `authRequired: true` will not narrow an app that is already\n * gated -- the boolean cannot tell \"private\" from \"org\", so the API\n * leaves the tier alone rather than risk widening a private app. Send\n * `visibility` to move between the gated tiers.\n */\n visibility?: Visibility;\n /** @deprecated Use `visibility`. false is equivalent to \"public\". */\n authRequired?: boolean;\n /** \"active\" or \"paused\". */\n status?: \"active\" | \"paused\";\n cpu?: number;\n memory?: number;\n instances?: number;\n /** Requests per minute per client, or null to remove the limit. */\n rateLimitPerMinute?: number | null;\n postgres?: boolean;\n storage?: boolean;\n email?: boolean;\n /** Stack label grouping this app with its siblings, or null to ungroup. */\n stack?: string | null;\n /**\n * Switches between \"container\" and \"serverless\". Like a resize, the\n * change applies on the next deploy, start or restart rather than\n * immediately -- the running version keeps serving until then. Refused\n * while the app has cron schedules or background jobs, which serverless\n * mode does not run.\n */\n mode?: AppMode;\n },\n ): Promise<App & { restartRequired?: boolean }> {\n const body: Record<string, unknown> = {};\n if (changes.name !== undefined) body.name = changes.name;\n if (changes.authRequired !== undefined) body.auth_required = changes.authRequired;\n if (changes.visibility !== undefined) body.visibility = changes.visibility;\n if (changes.status !== undefined) body.status = changes.status;\n if (changes.cpu !== undefined) body.cpu = changes.cpu;\n if (changes.memory !== undefined) body.memory = changes.memory;\n if (changes.instances !== undefined) body.instances = changes.instances;\n // Sent when explicitly null, which is how a limit is removed -- omitting it\n // would leave the old limit in place and look like the call did nothing.\n if (changes.rateLimitPerMinute !== undefined) {\n body.rate_limit_per_minute = changes.rateLimitPerMinute;\n }\n if (changes.postgres !== undefined) body.postgres_enabled = changes.postgres;\n if (changes.storage !== undefined) body.storage_enabled = changes.storage;\n if (changes.email !== undefined) body.email_enabled = changes.email;\n // Like the rate limit: explicit null ungroups, omitted leaves it alone.\n if (changes.stack !== undefined) body.stack = changes.stack;\n if (changes.mode !== undefined) body.mode = changes.mode;\n\n const res = await this.client.request<ApiResponse<App & { restartRequired?: boolean }>>(\n \"PATCH\",\n `/apps/${appId}`,\n body,\n );\n return res.data;\n }\n\n async list(orgId: string): Promise<App[]> {\n const res = await this.client.request<ApiResponse<App[]>>(\n \"GET\",\n `/apps?org_id=${encodeURIComponent(orgId)}`,\n );\n return res.data;\n }\n\n async get(id: string): Promise<App> {\n const res = await this.client.request<ApiResponse<App>>(\"GET\", `/apps/${id}`);\n return res.data;\n }\n\n /**\n * Deletes the app and tears down its AWS resources.\n *\n * Teardown is fault-isolated per resource, so a partial failure still deletes\n * the app and reports what leaked in `warnings` (HTTP 207) rather than\n * aborting. An empty array means everything was released.\n */\n async delete(id: string): Promise<{ warnings: { step: string; error?: string }[] }> {\n const res = await this.client.request<{\n data: App;\n warnings?: { step: string; error?: string }[];\n }>(\"DELETE\", `/apps/${id}`);\n return { warnings: res?.warnings ?? [] };\n }\n\n /**\n * Pushes a snapshot.\n *\n * `files` carries text. Anything that is not valid UTF-8 \u2014 an image, a\n * favicon, a compiled binary \u2014 must go in `binaryFiles` as base64, because\n * JSON cannot carry arbitrary bytes in a string and a value that is silently\n * re-encoded arrives corrupted. The two are separate maps rather than one\n * with a flag: base64 is not detectable (`\"data\"` is valid base64 and valid\n * text), so the encoding has to be stated by the caller.\n */\n async push(\n appId: string,\n files: Record<string, string>,\n message?: string,\n binaryFiles?: Record<string, string>,\n ): Promise<Snapshot> {\n const res = await this.client.request<ApiResponse<Snapshot>>(\"POST\", `/apps/${appId}/push`, {\n files,\n message,\n // Request bodies are not camelized \u2014 only responses are \u2014 so the wire\n // name has to be written out here.\n ...(binaryFiles && Object.keys(binaryFiles).length > 0 ? { binary_files: binaryFiles } : {}),\n });\n return res.data;\n }\n\n async deploy(appId: string, snapshotId: string, environment?: Environment): Promise<Deployment> {\n const res = await this.client.request<ApiResponse<Deployment>>(\"POST\", `/apps/${appId}/deploy`, {\n snapshot_id: snapshotId,\n environment,\n });\n return res.data;\n }\n\n async promote(deploymentId: string): Promise<Deployment> {\n const res = await this.client.request<ApiResponse<Deployment>>(\"POST\", `/deployments/${deploymentId}/promote`, {});\n return res.data;\n }\n\n async rollback(appId: string): Promise<Deployment> {\n const res = await this.client.request<ApiResponse<Deployment>>(\"POST\", `/apps/${appId}/rollback`, {});\n return res.data;\n }\n\n async checkDeployment(deploymentId: string): Promise<Deployment> {\n const res = await this.client.request<ApiResponse<Deployment>>(\"POST\", `/deployments/${deploymentId}/status`, {});\n return res.data;\n }\n\n /**\n * A page of the app's logs, newest last.\n *\n * Returns a page rather than a bare array of entries, which is what this used to\n * be: an array cannot say whether there is more to fetch, and the endpoint\n * behind it silently answered with the newest few log streams as though they\n * were the whole app. To page, pass the previous response's `nextToken`; to\n * follow, pass the last event's `timestamp` as `since`.\n *\n * `since`/`until` accept an ISO timestamp, epoch millis, or an offset like\n * `15m`. `q` is a literal substring; `pattern` is CloudWatch filter syntax --\n * pass one or the other, not both.\n */\n async logs(\n appId: string,\n opts?: {\n limit?: number;\n since?: string;\n until?: string;\n q?: string;\n pattern?: string;\n /** Log stream prefix, to read one instance instead of all of them. */\n instance?: string;\n nextToken?: string;\n },\n ): Promise<Camelize<LogPage>> {\n const params = new URLSearchParams();\n if (opts?.limit) params.set(\"limit\", String(opts.limit));\n if (opts?.since) params.set(\"since\", opts.since);\n if (opts?.until) params.set(\"until\", opts.until);\n if (opts?.q) params.set(\"q\", opts.q);\n if (opts?.pattern) params.set(\"pattern\", opts.pattern);\n if (opts?.instance) params.set(\"instance\", opts.instance);\n if (opts?.nextToken) params.set(\"next_token\", opts.nextToken);\n const query = params.toString();\n const path = `/apps/${appId}/logs${query ? `?${query}` : \"\"}`;\n const res = await this.client.request<ApiResponse<Camelize<LogPage>>>(\"GET\", path);\n return res.data;\n }\n\n /**\n * Follows the app's logs, yielding each line as it appears.\n *\n * Polling rather than the API's SSE tail: an async generator works in node\n * without `EventSource`, and it carries the bearer token the same way as every\n * other call. The cursor is the last event's timestamp and the window is\n * reopened *at* it rather than after it -- several lines share a millisecond, so\n * advancing past it would drop the rest of them -- with the boundary\n * millisecond's lines remembered to suppress the repeat.\n */\n async *followLogs(\n appId: string,\n opts?: { since?: string; q?: string; pattern?: string; instance?: string; intervalMs?: number },\n ): AsyncGenerator<LogEntry> {\n const interval = opts?.intervalMs ?? 2000;\n let since = opts?.since ?? \"60s\";\n let seenAtCursor = new Set<string>();\n\n while (true) {\n const page = await this.logs(appId, {\n since,\n limit: 500,\n q: opts?.q,\n pattern: opts?.pattern,\n instance: opts?.instance,\n });\n\n for (const event of page.events) {\n const key = `${event.stream || \"\"} ${event.message}`;\n if (event.timestamp === since) {\n if (seenAtCursor.has(key)) continue;\n seenAtCursor.add(key);\n } else {\n since = event.timestamp;\n seenAtCursor = new Set([key]);\n }\n yield event;\n }\n\n await new Promise((r) => setTimeout(r, interval));\n }\n }\n\n /**\n * Traffic, latency and resource use over a window.\n *\n * `since`/`until` take the same values as `logs`, deliberately: an owner who\n * spots a spike here reads the logs for the same period next.\n *\n * Note the series keys arrive camelized like every other field on this client,\n * so the API's `client_errors` is `clientErrors` and `latency_p95` is\n * `latencyP95`.\n */\n async metrics(\n appId: string,\n opts?: { since?: string; until?: string },\n ): Promise<Camelize<AppMetrics>> {\n const params = new URLSearchParams();\n if (opts?.since) params.set(\"since\", opts.since);\n if (opts?.until) params.set(\"until\", opts.until);\n const query = params.toString();\n const res = await this.client.request<ApiResponse<Camelize<AppMetrics>>>(\n \"GET\",\n `/apps/${appId}/metrics${query ? `?${query}` : \"\"}`,\n );\n return res.data;\n }\n\n /**\n * Live runtime state, including the container's exit code when it has crashed.\n * A deployment stays \"running\" in the database after the task dies, so this is\n * the only way to tell a healthy app from a crash loop.\n */\n /**\n * What this app cost in a month (`YYYY-MM`, default the month in progress),\n * priced at list rates with no allowance applied -- the plan's included usage\n * belongs to the org, so per-app figures would otherwise sum past the invoice.\n */\n async usage(appId: string, period?: string): Promise<AppUsage> {\n const query = period ? `?period=${encodeURIComponent(period)}` : \"\";\n const res = await this.client.request<ApiResponse<AppUsage>>(\n \"GET\",\n `/apps/${appId}/usage${query}`,\n );\n return res.data;\n }\n\n async health(appId: string): Promise<AppHealth> {\n const res = await this.client.request<ApiResponse<AppHealth>>(\"GET\", `/apps/${appId}/health`);\n return res.data;\n }\n\n /**\n * Build output for a deployment. Poll with the previous response's `next_token`\n * to stream a build as it runs.\n */\n async buildLogs(deploymentId: string, nextToken?: string): Promise<Camelize<BuildLogs>> {\n const query = nextToken ? `?next_token=${encodeURIComponent(nextToken)}` : \"\";\n const res = await this.client.request<ApiResponse<Camelize<BuildLogs>>>(\n \"GET\",\n `/deployments/${deploymentId}/build-logs${query}`,\n );\n return res.data;\n }\n}\n\nclass ApiKeysNamespace {\n constructor(private client: CloudClient) {}\n\n /**\n * Creates a key. The plaintext `key` is present on this response only -- the\n * server stores a hash and cannot return it again.\n */\n async create(opts: {\n name: string;\n orgId: string;\n expiresInDays?: number;\n /**\n * Pass an app id for a **service token**: a credential pinned to that one app,\n * for code running inside it. It cannot reach another app, issue further keys,\n * list the org's keys or revoke anything -- which is what makes it safe to\n * ship inside a container. A key without this acts as its creator across\n * every app in the org, so do not put one in a deployed app.\n */\n appId?: string;\n }): Promise<ApiKeyWithSecret> {\n const res = await this.client.request<ApiResponse<ApiKeyWithSecret>>(\"POST\", \"/api-keys\", {\n name: opts.name,\n org_id: opts.orgId,\n expires_in_days: opts.expiresInDays,\n app_id: opts.appId,\n });\n return res.data;\n }\n\n async list(orgId: string): Promise<ApiKey[]> {\n const res = await this.client.request<ApiResponse<ApiKey[]>>(\n \"GET\",\n `/api-keys?org_id=${encodeURIComponent(orgId)}`,\n );\n return res.data;\n }\n\n async revoke(id: string): Promise<void> {\n await this.client.request<void>(\"DELETE\", `/api-keys/${id}`);\n }\n}\n\nclass SecretsNamespace {\n constructor(private client: CloudClient) {}\n\n async set(appId: string, key: string, value: string): Promise<Secret> {\n const res = await this.client.request<ApiResponse<Secret>>(\"POST\", `/apps/${appId}/secrets`, {\n key,\n value,\n });\n return res.data;\n }\n\n async list(appId: string): Promise<Secret[]> {\n const res = await this.client.request<ApiResponse<Secret[]>>(\"GET\", `/apps/${appId}/secrets`);\n return res.data;\n }\n\n async delete(appId: string, key: string): Promise<void> {\n await this.client.request<void>(\"DELETE\", `/apps/${appId}/secrets/${key}`);\n }\n}\n\nexport interface StoredObject {\n key: string;\n size: number;\n lastModified: string | null;\n etag: string | null;\n}\n\n/**\n * Object storage for an app.\n *\n * Bytes never pass through the control API: it hands out a presigned S3 URL and\n * the transfer goes direct. `upload` and `download` wrap that two-step for the\n * common case; `presign` is there for handing a URL to a browser so an upload\n * skips the app's own server entirely.\n *\n * Best reached with a **service token** (`apiKeys.create({ appId })`) from inside\n * the app, so the credential in the container cannot touch anything else.\n */\nclass StorageNamespace {\n constructor(private client: CloudClient) {}\n\n async list(\n appId: string,\n opts: { prefix?: string; limit?: number; cursor?: string } = {},\n ): Promise<{ objects: StoredObject[]; nextCursor: string | null }> {\n const q = new URLSearchParams();\n if (opts.prefix) q.set(\"prefix\", opts.prefix);\n if (opts.limit) q.set(\"limit\", String(opts.limit));\n if (opts.cursor) q.set(\"cursor\", opts.cursor);\n const query = q.toString() ? `?${q}` : \"\";\n const res = await this.client.request<\n ApiResponse<{ objects: StoredObject[]; nextCursor: string | null }>\n >(\"GET\", `/apps/${appId}/storage/objects${query}`);\n return res.data;\n }\n\n /** Every object under the prefix, paged through for you. */\n async listAll(appId: string, prefix?: string): Promise<StoredObject[]> {\n const all: StoredObject[] = [];\n let cursor: string | undefined;\n do {\n const page = await this.list(appId, { prefix, cursor, limit: 1000 });\n all.push(...page.objects);\n cursor = page.nextCursor ?? undefined;\n } while (cursor);\n return all;\n }\n\n async usage(appId: string): Promise<{ objects: number; bytes: number }> {\n const res = await this.client.request<ApiResponse<{ objects: number; bytes: number }>>(\n \"GET\",\n `/apps/${appId}/storage/usage`,\n );\n return res.data;\n }\n\n /** Metadata for one object, or null when it is not there. */\n async head(appId: string, key: string): Promise<StoredObject | null> {\n try {\n const res = await this.client.request<ApiResponse<StoredObject>>(\n \"GET\",\n `/apps/${appId}/storage/object?key=${encodeURIComponent(key)}`,\n );\n return res.data;\n } catch (e) {\n if (e instanceof CloudError && e.status === 404) return null;\n throw e;\n }\n }\n\n async delete(appId: string, key: string): Promise<void> {\n await this.client.request<ApiResponse<unknown>>(\n \"DELETE\",\n `/apps/${appId}/storage/object?key=${encodeURIComponent(key)}`,\n );\n }\n\n /**\n * A short-lived URL for one key and one method. Hand a `put` URL to a browser to\n * upload without the bytes touching the app, or a `get` URL to share a private\n * object for a few minutes. Treat it as a credential: anyone holding it has that\n * access until it expires.\n */\n async presign(\n appId: string,\n key: string,\n opts: {\n method?: \"put\" | \"get\";\n expiresIn?: number;\n contentType?: string;\n download?: string;\n } = {},\n ): Promise<{ url: string; method: string; key: string; expiresIn: number }> {\n const res = await this.client.request<\n ApiResponse<{ url: string; method: string; key: string; expiresIn: number }>\n >(\"POST\", `/apps/${appId}/storage/presign`, {\n key,\n method: opts.method ?? \"get\",\n expires_in: opts.expiresIn,\n content_type: opts.contentType,\n download: opts.download,\n });\n return res.data;\n }\n\n /**\n * Presigns and uploads in one call.\n *\n * `contentType` is signed into the URL, so it is sent on the PUT as well --\n * omitting it there would make S3 reject the signature with an opaque 403.\n */\n async upload(\n appId: string,\n key: string,\n body: Uint8Array | ArrayBuffer | string | Blob,\n opts: { contentType?: string } = {},\n ): Promise<StoredObject> {\n const signed = await this.presign(appId, key, {\n method: \"put\",\n contentType: opts.contentType,\n });\n const res = await fetch(signed.url, {\n method: \"PUT\",\n headers: opts.contentType ? { \"Content-Type\": opts.contentType } : undefined,\n body: body as BodyInit,\n });\n if (!res.ok) {\n // S3's own error is XML, not the platform's JSON envelope, so it is passed\n // through as text rather than parsed into a CloudError shape it does not have.\n throw new CloudError(\n `Upload failed with status ${res.status}: ${(await res.text()).slice(0, 300)}`,\n \"upload_failed\",\n res.status,\n );\n }\n return { key, size: 0, lastModified: null, etag: res.headers.get(\"etag\") };\n }\n\n /** Presigns and fetches. Returns the raw bytes; decode as the caller sees fit. */\n async download(appId: string, key: string): Promise<Uint8Array> {\n const signed = await this.presign(appId, key, { method: \"get\" });\n const res = await fetch(signed.url);\n if (!res.ok) {\n throw new CloudError(\n `Download failed with status ${res.status}`,\n res.status === 404 ? \"not_found\" : \"download_failed\",\n res.status,\n );\n }\n return new Uint8Array(await res.arrayBuffer());\n }\n}\n\nexport interface EmailQuota {\n sent: number;\n quota: number;\n remaining: number;\n /** The API sends `resets_at`; `camelizeDeep` renames it on the way in. */\n resetsAt: string;\n}\n\nexport interface SentEmail {\n messageId: string | null;\n to: string[];\n from: string;\n quota: EmailQuota;\n}\n\n/**\n * Transactional email sent by the platform on the app's behalf.\n *\n * There is no `from`: mail goes out as `app-<slug>@<platform domain>`, because an\n * app able to choose its own From could send a credible password-reset email as\n * the platform. Use `replyTo` to route replies to your own address.\n */\nclass EmailNamespace {\n constructor(private client: CloudClient) {}\n\n /**\n * Sends one message, to up to 50 recipients, as a single all-or-nothing send.\n *\n * Throws `CloudError` with status 429 once the app's daily quota is used up, and\n * 502 when the provider rejected the message -- the message text carries the\n * reason, which while the sending account is sandboxed is usually that the\n * recipient address is not verified.\n */\n async send(\n appId: string,\n message: {\n to: string | string[];\n subject: string;\n text?: string;\n html?: string;\n replyTo?: string;\n },\n ): Promise<SentEmail> {\n const res = await this.client.request<ApiResponse<SentEmail>>(\n \"POST\",\n `/apps/${appId}/email`,\n {\n to: message.to,\n subject: message.subject,\n text: message.text,\n html: message.html,\n reply_to: message.replyTo,\n },\n );\n return res.data;\n }\n\n /** Sends used today and what is left. Resets at UTC midnight. */\n async quota(appId: string): Promise<EmailQuota> {\n const res = await this.client.request<ApiResponse<EmailQuota>>(\n \"GET\",\n `/apps/${appId}/email/quota`,\n );\n return res.data;\n }\n}\n\n/**\n * Background jobs.\n *\n * A job is delivered as a POST to a path on the app itself, so the \"worker\" is a\n * route in the app the agent already wrote. Delivery is at least once -- retries\n * mean a handler can see the same job twice -- which is what `idempotencyKey`\n * on the enqueue side and the `x-videan-job-attempt` header on the receiving side\n * are for.\n */\nclass JobsNamespace {\n constructor(private client: CloudClient) {}\n\n /**\n * Enqueues a job.\n *\n * Pass `idempotencyKey` when the caller may retry this enqueue: a repeat returns\n * the job already queued, with `deduplicated: true`, instead of a second copy of\n * the work.\n */\n async enqueue(\n appId: string,\n options: {\n queue?: string;\n /** Path on the app the job is POSTed to. Defaults to `/jobs`. */\n path?: string;\n method?: \"POST\" | \"PUT\" | \"PATCH\";\n payload?: unknown;\n maxAttempts?: number;\n timeoutSeconds?: number;\n delaySeconds?: number;\n idempotencyKey?: string;\n } = {},\n ): Promise<Job> {\n const res = await this.client.request<ApiResponse<Job>>(\"POST\", `/apps/${appId}/jobs`, {\n queue: options.queue,\n path: options.path,\n method: options.method,\n payload: options.payload,\n max_attempts: options.maxAttempts,\n timeout_seconds: options.timeoutSeconds,\n delay_seconds: options.delaySeconds,\n idempotency_key: options.idempotencyKey,\n });\n return res.data;\n }\n\n /** A page of jobs, newest first, with the queue depth per status alongside. */\n async list(\n appId: string,\n options: { status?: string; queue?: string; limit?: number } = {},\n ): Promise<{ jobs: Job[]; counts: Record<string, number> }> {\n const params = new URLSearchParams();\n if (options.status) params.set(\"status\", options.status);\n if (options.queue) params.set(\"queue\", options.queue);\n if (options.limit) params.set(\"limit\", String(options.limit));\n const query = params.toString();\n const res = await this.client.request<ApiResponse<{ jobs: Job[]; counts: Record<string, number> }>>(\n \"GET\",\n `/apps/${appId}/jobs${query ? `?${query}` : \"\"}`,\n );\n return res.data;\n }\n\n async get(appId: string, jobId: string): Promise<Job> {\n const res = await this.client.request<ApiResponse<Job>>(\"GET\", `/apps/${appId}/jobs/${jobId}`);\n return res.data;\n }\n\n /** Runs a dead job again, from a clean attempt count. */\n async retry(appId: string, jobId: string): Promise<Job> {\n const res = await this.client.request<ApiResponse<Job>>(\n \"POST\",\n `/apps/${appId}/jobs/${jobId}/retry`,\n );\n return res.data;\n }\n\n /** Cancels a job that has not started. Throws 409 once it is running. */\n async cancel(appId: string, jobId: string): Promise<Job> {\n const res = await this.client.request<ApiResponse<Job>>(\n \"POST\",\n `/apps/${appId}/jobs/${jobId}/cancel`,\n );\n return res.data;\n }\n}\n\nclass OrgsNamespace {\n constructor(private client: CloudClient) {}\n\n async list(): Promise<Organization[]> {\n const res = await this.client.request<ApiResponse<Organization[]>>(\"GET\", \"/orgs\");\n return res.data;\n }\n\n async create(name: string): Promise<Organization> {\n const res = await this.client.request<ApiResponse<Organization>>(\"POST\", \"/orgs\", { name });\n return res.data;\n }\n\n async get(id: string): Promise<Organization> {\n const res = await this.client.request<ApiResponse<Organization>>(\"GET\", `/orgs/${id}`);\n return res.data;\n }\n\n async members(id: string): Promise<OrgMember[]> {\n const res = await this.client.request<ApiResponse<OrgMember[]>>(\"GET\", `/orgs/${id}/members`);\n return res.data;\n }\n\n async invite(id: string, email: string, role: \"admin\" | \"member\" = \"member\"): Promise<void> {\n await this.client.request<ApiResponse<unknown>>(\"POST\", `/orgs/${id}/members`, { email, role });\n }\n\n async removeMember(id: string, userId: string): Promise<void> {\n await this.client.request<ApiResponse<unknown>>(\"DELETE\", `/orgs/${id}/members/${userId}`);\n }\n\n /**\n * Metered usage and the priced invoice for a month (`YYYY-MM`, default the month\n * in progress). Read-only: nothing in the SDK changes a plan or takes a payment.\n */\n async usage(id: string, period?: string): Promise<OrgUsage> {\n const query = period ? `?period=${encodeURIComponent(period)}` : \"\";\n const res = await this.client.request<ApiResponse<OrgUsage>>(\n \"GET\",\n `/orgs/${id}/usage${query}`,\n );\n return res.data;\n }\n}\n", "import type { Invoice, UsageLine } from \"./pricing.js\";\n\n// --- Enums ---\n\nexport enum DeploymentStatus {\n Building = \"building\",\n Deploying = \"deploying\",\n Running = \"running\",\n Failed = \"failed\",\n Stopped = \"stopped\",\n /**\n * Was serving, then its task died on its own -- OOM, a panic, an ECS\n * replacement. Distinct from Failed, which means the deploy never worked at\n * all: the difference is what the owner should do about it, and only one of\n * the two has an image known to have booted successfully.\n */\n Crashed = \"crashed\",\n}\n\nexport enum Environment {\n Preview = \"preview\",\n Production = \"production\",\n}\n\n/**\n * Which compute an app runs on.\n *\n * Both modes meet the same one contract -- serve HTTP on port 3000, behind the\n * same edge gate, with the same snapshot/promote/rollback semantics -- so this\n * is a choice about economics and constraints, not about how an app is written:\n *\n * - `container`: an ECS Fargate task. Always on, billed by the second, and the\n * only mode that can hold a WebSocket, run work longer than 15 minutes, keep\n * in-process state between requests, or build from a custom Dockerfile.\n * - `serverless`: an AWS Lambda container image behind the Lambda Web Adapter.\n * Scales to zero and bills per GB-ms, at the cost of a cold start on the\n * first request after idle. Request/response HTTP only.\n *\n * The agent picks explicitly. There is deliberately no auto-detection: a mode\n * guessed from the files is a mode that can silently differ from what was asked\n * for, which is the exact class of surprise the agent guide exists to prevent.\n * An omitted mode is `container` -- what every app was before the field existed.\n * A *misspelled* one is a 400, not a default.\n *\n * `as const` rather than an annotated array, so zod can take this list directly\n * as an enum -- `z.enum` needs a non-empty tuple type, which `AppMode[]` is not --\n * and the type is derived from the values, the way `VISIBILITIES` does it, so\n * there is one list and nothing to keep in step by hand.\n */\nexport const APP_MODES = [\"container\", \"serverless\"] as const;\n\nexport type AppMode = (typeof APP_MODES)[number];\n\n/**\n * Narrows a mode that crossed a boundary as `string` -- the column is text, so a\n * stored value is only a claim.\n *\n * Note what this deliberately does not do: it does not coerce. Readers fall back\n * to `container` for an unrecognised value, because that is the mode that\n * predates the column and cannot surprise anyone; the write path rejects instead.\n */\nexport function isAppMode(value: string): value is AppMode {\n return (APP_MODES as readonly string[]).includes(value);\n}\n\n/** The stored mode of an app or deployment, defaulted for rows that predate it. */\nexport function appModeOf(value: string | null | undefined): AppMode {\n return value && isAppMode(value) ? value : \"container\";\n}\n\nexport enum Role {\n Admin = \"admin\",\n Member = \"member\",\n}\n\n// --- Core entities ---\n\nexport interface App {\n id: string;\n name: string;\n slug: string;\n orgId: string;\n status: string;\n postgresEnabled: boolean;\n /** Whether the app has an object-storage prefix it can read and write. */\n storageEnabled?: boolean;\n /** Whether the app may send mail through the platform's verified identity. */\n emailEnabled?: boolean;\n /**\n * Legacy view of `visibility`: false only for the `public` tier. Kept because\n * an already-deployed edge router reads it and nothing else.\n */\n authRequired: boolean;\n /** Who may reach the running app. See `Visibility` in `visibility.ts`. */\n visibility?: string;\n /** ECS task size per instance. Null means the platform default for the runtime. */\n cpu?: number | null;\n memory?: number | null;\n instances?: number;\n /** Requests per minute per client, enforced at the edge. Null means unlimited. */\n rateLimitPerMinute?: number | null;\n /**\n * Groups the apps of one logical system (a split frontend + backend) under a\n * shared label. Purely descriptive -- the console clusters same-stack apps in\n * the list; nothing in routing, auth or billing reads it. Null = ungrouped.\n */\n stack?: string | null;\n /** Which compute this app runs on. See `AppMode`. */\n mode?: string;\n customDomain?: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface Snapshot {\n id: string;\n appId: string;\n files: Record<string, string>;\n createdAt: string;\n message?: string;\n}\n\nexport interface Deployment {\n id: string;\n appId: string;\n snapshotId: string;\n status: string;\n environment: string;\n /** Only on the response to the deploy that started it; not listed afterwards. */\n buildId?: string | null;\n /** The app's hostname, once this deployment is the promoted one. */\n url?: string | null;\n /**\n * This deployment's own hostname, live from the moment its tasks are up --\n * before it is promoted. What an agent checks its own work against without\n * production riding on the answer.\n */\n previewUrl?: string | null;\n imageTag?: string | null;\n /** The execution mode this deployment ran under. Absent means `container`. */\n mode?: string | null;\n /** Serverless only: the Lambda version this deployment pinned. */\n functionVersion?: string | null;\n /** Machine-readable failure cause, for a caller deciding what to fix. */\n failureReason?: string | null;\n failureDetail?: unknown;\n restartCount?: number;\n lastRestartAt?: string | null;\n createdAt: string;\n updatedAt?: string | null;\n completedAt?: string | null;\n error?: string;\n}\n\n/**\n * One background job.\n *\n * `status` is `queued` | `running` | `succeeded` | `dead` | `cancelled`. A failed\n * attempt with retries left is back in `queued` with `attempts` advanced, `error`\n * holding what went wrong last and `runAt` saying when it will be tried again --\n * there is no separate `failed` state, because a retriable failure and a pending\n * job are the same thing waiting.\n */\nexport interface Job {\n id: string;\n queue: string;\n path: string;\n method: string;\n payload: unknown;\n status: string;\n attempts: number;\n maxAttempts: number;\n runAt: string;\n timeoutSeconds: number;\n /** The handler's HTTP status on the last attempt. Null if it never answered. */\n statusCode?: number | null;\n error?: string | null;\n durationMs?: number | null;\n idempotencyKey?: string | null;\n createdAt: string;\n startedAt?: string | null;\n finishedAt?: string | null;\n /**\n * True when this enqueue matched an idempotency key already used, so the job\n * returned is the earlier one and nothing was created.\n */\n deduplicated?: boolean;\n}\n\n/**\n * One metered quantity. A list rather than a `metric -> quantity` map because\n * metric names are snake_case, and the SDK rewrites snake_case *keys* to camelCase\n * while leaving values alone -- as a key, `task_seconds` would arrive spelled\n * differently than the same name inside an invoice line.\n */\nexport interface UsageQuantity {\n metric: string;\n quantity: number;\n}\n\nexport interface AppUsageShare {\n /** Null for usage recorded by apps that have since been deleted. */\n appId: string | null;\n slug?: string;\n name?: string;\n metrics: UsageQuantity[];\n /** This app's share at list price, with no allowance applied. */\n cents: number;\n}\n\nexport interface OrgUsage {\n orgId: string;\n plan: string;\n periodStart: string;\n periodEnd: string;\n metrics: UsageQuantity[];\n apps: AppUsageShare[];\n invoice: Invoice;\n /** `invoice.totalCents` formatted, so a terminal never divides by 100 itself. */\n total: string;\n}\n\nexport interface AppUsage {\n appId: string;\n orgId: string;\n periodStart: string;\n periodEnd: string;\n metrics: UsageQuantity[];\n lines: UsageLine[];\n cents: number;\n cost: string;\n /** Per-day rows, newest first, so a spike is attributable to a day. */\n daily: Array<{ date: string; metric: string; quantity: number }>;\n}\n\n/** One entry of the public plan catalogue (`GET /v1/plans`). */\nexport interface PlanInfo {\n id: string;\n name: string;\n baseCents: number;\n base: string;\n includedUsageCents: number;\n includedUsage: string;\n limits: {\n apps: number;\n instancesPerApp: number;\n maxCpu: number;\n maxMemory: number;\n maxStorageMb: number;\n postgres: boolean;\n customDomains: boolean;\n };\n}\n\nexport interface Secret {\n key: string;\n appId: string;\n createdAt: string;\n updatedAt: string;\n}\n\n/** Live ECS state for an app's current deployment. */\nexport interface AppHealth {\n app_id: string;\n healthy: boolean;\n /** \"running\" | \"stopped\" | \"pending\" | \"building\" | \"not_deployed\" | \"no_task\" | \"task_gone\" */\n state: string;\n deployment_id: string | null;\n url: string | null;\n task: {\n status: string;\n desired_status?: string;\n health: string;\n ip?: string;\n port: number;\n stopped_reason?: string;\n stop_code?: string;\n exit_code?: number;\n container_reason?: string;\n started_at?: string;\n stopped_at?: string;\n } | null;\n}\n\nexport interface BuildPhase {\n name: string;\n status?: string;\n durationSeconds?: number;\n}\n\nexport interface BuildLogs {\n build_id: string;\n build_status: string;\n deployment_status: string;\n phases: BuildPhase[];\n lines: { timestamp: string; message: string }[];\n /** Pass back as `next_token` to fetch only what has been appended since. */\n next_token?: string;\n}\n\nexport interface ApiKey {\n id: string;\n name: string;\n orgId: string;\n keyPrefix: string;\n /**\n * `service` is pinned to `appId` and refused by everything outside that app;\n * `user` acts as its creator across the whole org.\n */\n kind?: \"user\" | \"service\";\n appId?: string | null;\n lastUsedAt?: string | null;\n expiresAt?: string | null;\n revokedAt?: string | null;\n createdAt: string;\n}\n\n/** Returned only by create -- `key` is never retrievable again. */\nexport interface ApiKeyWithSecret extends ApiKey {\n key: string;\n}\n\nexport interface Organization {\n id: string;\n name: string;\n slug: string;\n plan: string;\n createdAt: string;\n}\n\nexport interface User {\n id: string;\n email: string;\n name: string;\n avatarUrl?: string;\n createdAt: string;\n}\n\n/** Shape returned by GET /v1/orgs/:id/members -- flattened, not a nested user. */\nexport interface OrgMember {\n user_id: string;\n role: string;\n joined_at: string;\n email?: string;\n name?: string;\n}\n\n// --- API request types ---\n\nexport interface CreateAppRequest {\n name: string;\n org_id: string;\n postgres_enabled?: boolean;\n storage_enabled?: boolean;\n email_enabled?: boolean;\n auth_required?: boolean;\n /** One of `VISIBILITIES`. Wins over `auth_required` when both are sent. */\n visibility?: string;\n /** Stack label grouping this app with its siblings (slug-shaped, \u226450 chars). */\n stack?: string;\n /** One of `APP_MODES`. Omitted means `container`; a bad value is a 400. */\n mode?: AppMode;\n}\n\nexport interface PushRequest {\n /** Path \u2192 UTF-8 text. */\n files: Record<string, string>;\n /**\n * Path \u2192 base64. Required for anything that is not valid UTF-8, since JSON\n * strings cannot carry arbitrary bytes. Separate from `files` because base64\n * is not detectable \u2014 `\"data\"` is both \u2014 so the caller has to declare it.\n */\n binary_files?: Record<string, string>;\n message?: string;\n}\n\nexport interface DeployRequest {\n snapshot_id: string;\n environment?: Environment;\n}\n\nexport interface PromoteRequest {\n deployment_id: string;\n}\n\nexport interface RollbackRequest {\n app_id: string;\n}\n\nexport interface SetSecretRequest {\n key: string;\n value: string;\n}\n\nexport interface DeleteSecretRequest {\n key: string;\n}\n\nexport interface InviteMemberRequest {\n email: string;\n role?: Role;\n}\n\nexport interface GetLogsRequest {\n app_id: string;\n follow?: boolean;\n since?: string;\n limit?: number;\n}\n\n// --- API response types ---\n\nexport interface ApiResponse<T> {\n data: T;\n meta?: {\n total?: number;\n page?: number;\n per_page?: number;\n };\n}\n\nexport interface ApiError {\n error: {\n code: string;\n message: string;\n details?: Record<string, unknown>;\n };\n}\n\nexport interface LogEntry {\n timestamp: string;\n /**\n * Inferred from the text of the line, not read from structured output: the\n * contract with an app is \"expose a port\", so nothing guarantees it logs levels\n * at all. Useful for colouring a viewer; not something to filter on -- search is\n * the filter that tells the truth.\n */\n level: string;\n message: string;\n /**\n * Log stream, which identifies the *instance* that produced the line. With an\n * app running several tasks behind one hostname this is the only way to tell\n * whether one container is misbehaving or all of them are.\n */\n stream?: string;\n source?: string;\n}\n\n/**\n * One page of an app's logs.\n *\n * A page rather than a bare array because there are two things a bare array\n * cannot say: that there is more to fetch, and that the scan stopped early. Both\n * were previously invisible -- the old endpoint read the newest few log streams\n * and returned them as though they were the whole app.\n */\nexport interface LogPage {\n events: LogEntry[];\n /** Pass back as `next_token` to continue forward through the same window. */\n next_token?: string;\n /** The page budget was reached before the end of the window. */\n truncated: boolean;\n /** The window actually searched, which may not be the one requested verbatim. */\n start: string;\n end?: string;\n}\n\n/** One bucket of one series. `t` is the bucket's start, not its end. */\nexport interface MetricPoint {\n t: string;\n v: number;\n}\n\n/**\n * An app's metrics over a window.\n *\n * `series` is keyed by metric id: `requests`, `client_errors`, `server_errors`,\n * `gate_denied` and `latency_avg`/`latency_p95` come from the edge router (the only\n * component that sees per-app traffic); `cpu` and `memory` come from ECS Container\n * Insights, in CPU units and megabytes respectively rather than percentages.\n *\n * A key is always present, with an empty array when nothing was reported -- an\n * absent key would make every consumer handle undefined. A gap in a series means\n * no traffic in that bucket, not zero latency.\n */\nexport interface AppMetrics {\n start: string;\n end: string;\n /** Bucket width in seconds. Derived from the window, so not always 60. */\n period: number;\n series: Record<string, MetricPoint[]>;\n totals: {\n requests: number;\n client_errors: number;\n server_errors: number;\n gate_denied: number;\n /** Null when nothing was reported: no requests is not the same as 0ms. */\n latency_avg_ms: number | null;\n /** Estimated from a bounded sample of requests, not from all of them. */\n latency_p95_ms: number | null;\n cpu_avg: number | null;\n memory_avg_mb: number | null;\n };\n}\n\nexport type CreateAppResponse = ApiResponse<App>;\nexport type ListAppsResponse = ApiResponse<App[]>;\nexport type GetAppResponse = ApiResponse<App>;\nexport type PushResponse = ApiResponse<Snapshot>;\nexport type DeployResponse = ApiResponse<Deployment>;\nexport type PromoteResponse = ApiResponse<Deployment>;\nexport type RollbackResponse = ApiResponse<Deployment>;\nexport type ListSecretsResponse = ApiResponse<Secret[]>;\nexport type SetSecretResponse = ApiResponse<Secret>;\nexport type GetOrgResponse = ApiResponse<Organization>;\nexport type ListMembersResponse = ApiResponse<OrgMember[]>;\nexport type InviteMemberResponse = ApiResponse<OrgMember>;\nexport type GetLogsResponse = ApiResponse<LogPage>;\n", "export const DEFAULT_PORT = 3000;\n\nexport const SUPPORTED_RUNTIMES = [\n \"node-20\",\n \"node-22\",\n \"python-3.11\",\n \"python-3.12\",\n \"bun-1\",\n \"deno-2\",\n] as const;\n\nexport type SupportedRuntime = (typeof SUPPORTED_RUNTIMES)[number];\n\nexport const DEPLOYMENT_STATUSES = [\n \"building\",\n \"deploying\",\n \"running\",\n \"failed\",\n // A task that was live and then died on its own -- OOM, panic, ECS\n // replacement. The reconcile pass sets it, so anything enumerating statuses\n // has to know about it.\n \"crashed\",\n \"stopped\",\n] as const;\n\nexport const ENVIRONMENTS = [\"preview\", \"production\"] as const;\n\nexport const ROLES = [\"admin\", \"member\"] as const;\n\nexport const DEFAULT_API_URL = \"https://api.videan.xyz/v1\";\n\n/** The zone every app's hostname hangs off. */\nexport const APP_DOMAIN = process.env.CLOUD_APP_DOMAIN || \"videan.xyz\";\n\n/**\n * The public URL of an app, from its slug.\n *\n * One definition, because it was previously written out by hand in three places\n * and one of them omitted the scheme -- so a deployment's `url` came back as a\n * bare `slug.videan.xyz` that no terminal, browser or `fetch` would follow,\n * while the app resource reported the same app as `https://slug.videan.xyz`.\n * HTTP redirects to HTTPS anyway, so the scheme is not optional in practice.\n */\nexport function appUrl(slug: string): string {\n return `https://${slug}.${APP_DOMAIN}`;\n}\n\n/**\n * Prefix for a single deployment's own hostname.\n *\n * Every deployment is reachable before it is promoted, which is what lets an\n * agent verify its own work without production riding on the answer. The label is\n * a hash of the deployment id rather than the id itself: nanoid's alphabet\n * contains `_` and uppercase, neither of which is legal in a DNS label, and\n * lowercasing or substituting would map two distinct deployments onto one\n * hostname -- serving whichever the router looked up first.\n *\n * The prefix keeps the namespace from colliding with app slugs: `*.videan.xyz`\n * is one flat namespace, and an app legitimately slugged the same as some\n * deployment's hash would otherwise steal its traffic.\n */\nexport const PREVIEW_LABEL_PREFIX = \"d-\";\n\nexport function previewUrl(label: string): string {\n return `https://${PREVIEW_LABEL_PREFIX}${label}.${APP_DOMAIN}`;\n}\n\n/** True for a hostname label that belongs to the preview namespace. */\nexport function isPreviewLabel(label: string): boolean {\n return label.startsWith(PREVIEW_LABEL_PREFIX);\n}\n\nexport const CONFIG_FILE_NAME = \"cloud.yaml\";\n\nexport const IGNORE_FILE_NAME = \".cloudignore\";\n\nexport const MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; // 50MB\n\nexport const MAX_FILES_PER_PUSH = 10_000;\n\n/**\n * Public mailbox providers that can never be claimed as an SSO domain. A\n * domain claim routes every login for that domain to one org's IdP and lets\n * that IdP assert addresses under it \u2014 fine for a domain the org owns,\n * account capture for a mailbox anyone can register on. Not exhaustive (it\n * cannot be); the common ones an admin might try are what matters.\n */\nexport const PUBLIC_EMAIL_DOMAINS = [\n \"gmail.com\",\n \"googlemail.com\",\n \"outlook.com\",\n \"hotmail.com\",\n \"live.com\",\n \"msn.com\",\n \"yahoo.com\",\n \"ymail.com\",\n \"icloud.com\",\n \"me.com\",\n \"mac.com\",\n \"aol.com\",\n \"proton.me\",\n \"protonmail.com\",\n \"pm.me\",\n \"zoho.com\",\n \"gmx.com\",\n \"gmx.net\",\n \"mail.com\",\n \"fastmail.com\",\n \"hey.com\",\n \"tutanota.com\",\n \"yandex.com\",\n \"qq.com\",\n \"163.com\",\n \"126.com\",\n \"naver.com\",\n] as const;\n", "/**\n * Runtime detection and Dockerfile generation.\n *\n * This is the single source of truth for \"what kind of app is this and how do we\n * containerize it\". The control API generates build contexts from it and the\n * builder service reports runtime from it; two implementations would eventually\n * disagree about the same snapshot.\n */\n\nexport type Runtime = \"custom\" | \"nextjs\" | \"nodejs\" | \"python\" | \"go\";\n\n/**\n * Every app is served on this port.\n *\n * The edge router resolves a task to `ip:APP_PORT`, so detection cannot pick a\n * per-runtime port -- a Python app on 8000 would simply be unroutable. Generated\n * Dockerfiles set PORT=3000 and EXPOSE 3000; a project supplying its own\n * Dockerfile must listen here too.\n */\nexport const APP_PORT = 3000;\n\nexport const RUNTIMES: Runtime[] = [\"custom\", \"nextjs\", \"nodejs\", \"python\", \"go\"];\n\n/**\n * Narrows a string that crossed a boundary as `string` -- the builder reports the\n * detected runtime as plain text, and a caller keying a table off it needs to know\n * the value is one of ours rather than assume it.\n */\nexport function isRuntime(value: string): value is Runtime {\n return (RUNTIMES as string[]).includes(value);\n}\n\nexport interface DetectionResult {\n runtime: Runtime;\n port: number;\n /** True when the project shipped its own Dockerfile and we generate nothing. */\n hasDockerfile: boolean;\n /** Why this runtime was chosen -- surfaced in build logs. */\n reason: string;\n}\n\nconst NEXT_CONFIG = /^next\\.config\\.(js|cjs|mjs|ts|mts)$/;\n\n/**\n * The AWS Lambda Web Adapter, injected into every generated image.\n *\n * It is what lets one contract (\"serve HTTP on port 3000\") run on Lambda at all:\n * Lambda speaks an invoke API, not HTTP, and the adapter translates between them\n * in front of an ordinary web server. The app itself needs no Lambda awareness,\n * which is the whole point -- the same code and the same image serve both\n * execution modes.\n *\n * Injected **unconditionally**, including for apps that will only ever run on\n * Fargate. `/opt/extensions` binaries are started by the Lambda runtime and by\n * nothing else, so under Fargate this is an unused ~4 MB file: inert, not\n * conditional. That is deliberate and load-bearing -- it means switching an app\n * between `container` and `serverless` is a pointer move over the image that\n * already exists, not a rebuild. A mode-conditional Dockerfile would make the\n * image mode-specific and turn every mode change into a full build.\n *\n * Pinned to an exact adapter version for the same reason every base image here is:\n * `:latest` would silently change what a rebuild of an unchanged snapshot produces.\n *\n * `AWS_LWA_READINESS_CHECK_PATH` is deliberately unset -- the adapter's default\n * is a TCP connect to the port, which is exactly the platform's contract. A path\n * would require every app to serve some agreed health route, which is a second\n * contract.\n */\nconst LAMBDA_ADAPTER = `COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter\nENV AWS_LWA_PORT=${APP_PORT}`;\n\n/**\n * Amazon's RDS CA bundle, installed into every generated Node image.\n *\n * The injected `DATABASE_URL` carries `sslmode=require`, which under libpq means\n * \"encrypt, do not verify\" -- so psycopg and pgx connect happily. `pg` >= 8.16\n * deliberately diverges and treats `require` as an alias for `verify-full`, and\n * the RDS roots are in no distribution's trust store, so **every** Node app that\n * opened its managed database died on `unable to get local issuer certificate`\n * after a successful build and a successful promotion. Reported as a crash, on\n * the one code path a tenant cannot work around from their own repo.\n *\n * Fixed by supplying the CA rather than by weakening the mode: `sslmode=no-verify`\n * would work for `pg` and is not a libpq mode at all, so it would break the two\n * runtimes that currently work, and one URL is injected for all of them. With the\n * bundle present, verify-full genuinely verifies.\n *\n * `ADD` fails the build if the fetch fails, which is the right direction: a\n * silently absent CA is the bug being fixed here.\n *\n * The `chmod` is what makes it work under Lambda as well as Fargate. Docker gives\n * a file fetched by `ADD <url>` mode 600 owned by root, and the generated node\n * images declare no `USER`, so under Fargate the app runs as root and reads it\n * fine -- but Lambda executes a container image as its own unprivileged sandbox\n * user, which cannot open a root-only file. Node's response to an unreadable\n * `NODE_EXTRA_CA_CERTS` is to warn and carry on with the default store, so the\n * symptom is not a startup failure but the original bug returning verbatim:\n * `unable to get local issuer certificate`, from an image whose environment\n * plainly names the bundle. Written as a `RUN` rather than `ADD --chmod=644`\n * because that flag needs BuildKit and CodeBuild builds these with the classic\n * builder.\n */\nconst RDS_CA = `# Lets a managed-Postgres connection be verified, not merely encrypted. See\n# packages/shared/src/detect.ts -- without this, \\`pg\\` cannot connect at all.\nADD https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem /etc/ssl/certs/rds-ca.pem\nRUN chmod 644 /etc/ssl/certs/rds-ca.pem\nENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/rds-ca.pem`;\n\n/**\n * Detects the runtime from a snapshot's file list.\n *\n * Paths are relative and may carry a leading \"./\". Order matters: a project can\n * satisfy several checks at once (a Next.js app has a package.json too), so the\n * most specific signal has to win.\n */\nexport function detectRuntime(files: string[]): DetectionResult {\n const set = new Set(files.map((f) => f.replace(/^\\.\\//, \"\")));\n\n // A hand-written Dockerfile always wins -- it is an explicit instruction.\n if (set.has(\"Dockerfile\") || set.has(\"dockerfile\")) {\n return {\n runtime: \"custom\",\n port: APP_PORT,\n hasDockerfile: true,\n reason: \"project provided a Dockerfile\",\n };\n }\n\n const hasPackageJson = set.has(\"package.json\");\n const hasNextConfig = [...set].some((f) => NEXT_CONFIG.test(f));\n const hasRequirements = set.has(\"requirements.txt\");\n const hasPyproject = set.has(\"pyproject.toml\");\n const hasGoMod = set.has(\"go.mod\");\n\n if (hasPackageJson && hasNextConfig) {\n return {\n runtime: \"nextjs\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found package.json and a next.config file\",\n };\n }\n\n if (hasPackageJson) {\n return {\n runtime: \"nodejs\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found package.json\",\n };\n }\n\n if (hasRequirements || hasPyproject) {\n return {\n runtime: \"python\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: hasRequirements ? \"found requirements.txt\" : \"found pyproject.toml\",\n };\n }\n\n if (hasGoMod) {\n return {\n runtime: \"go\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found go.mod\",\n };\n }\n\n throw new Error(\n \"Could not detect a runtime. Include a package.json, requirements.txt, \" +\n \"pyproject.toml, go.mod, or your own Dockerfile.\",\n );\n}\n\n/** The Python entry point to run, in order of preference. */\nfunction pythonEntry(files: string[]): string | null {\n const set = new Set(files.map((f) => f.replace(/^\\.\\//, \"\")));\n for (const candidate of [\"main.py\", \"app.py\", \"server.py\", \"manage.py\"]) {\n if (set.has(candidate)) return candidate;\n }\n return null;\n}\n\n/**\n * Generates the Dockerfile for a detected runtime.\n *\n * Throws for `custom`: that project has its own Dockerfile and overwriting it\n * would silently discard the author's build.\n */\nexport function generateDockerfile(files: string[], detection?: DetectionResult): string {\n const detected = detection ?? detectRuntime(files);\n\n if (detected.runtime === \"custom\") {\n throw new Error(\"Project supplies its own Dockerfile; nothing to generate\");\n }\n\n switch (detected.runtime) {\n case \"nextjs\":\n return `FROM public.ecr.aws/docker/library/node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm install\nCOPY . .\nRUN npm run build\n${RDS_CA}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV HOSTNAME=0.0.0.0\nEXPOSE ${APP_PORT}\nCMD [\"npm\", \"start\"]\n`;\n\n case \"nodejs\":\n return `FROM public.ecr.aws/docker/library/node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\n# npm ci needs a lockfile; fall back to install when there is none.\nRUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi\nCOPY . .\n${RDS_CA}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV HOST=0.0.0.0\nEXPOSE ${APP_PORT}\nCMD [\"npm\", \"start\"]\n`;\n\n case \"python\": {\n const entry = pythonEntry(files);\n // No recognisable entry point: install deps and let the image fail loudly\n // at start rather than guessing a module name.\n const cmd = entry\n ? `CMD [\"python\", \"${entry}\"]`\n : `CMD [\"sh\", \"-c\", \"echo 'No entry point found (expected main.py, app.py, server.py or manage.py)' >&2; exit 1\"]`;\n const install = `RUN if [ -f requirements.txt ]; then pip install --no-cache-dir -r requirements.txt; else pip install --no-cache-dir .; fi`;\n return `FROM public.ecr.aws/docker/library/python:3.12-slim\nWORKDIR /app\nCOPY . .\n${install}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV PYTHONUNBUFFERED=1\nEXPOSE ${APP_PORT}\n${cmd}\n`;\n }\n\n case \"go\":\n return `FROM public.ecr.aws/docker/library/golang:1.22-alpine AS builder\nWORKDIR /build\nCOPY go.* ./\nRUN go mod download\nCOPY . .\nRUN CGO_ENABLED=0 go build -o app .\n\nFROM public.ecr.aws/docker/library/alpine:3.20\nWORKDIR /app\nCOPY --from=builder /build/app .\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nEXPOSE ${APP_PORT}\nCMD [\"./app\"]\n`;\n }\n}\n\n/**\n * Derives an app's URL slug from its name.\n *\n * The slug becomes a DNS label (`<slug>.videan.xyz`), so it must be lowercase\n * alphanumeric with inner hyphens and at most 63 characters.\n */\nexport function slugify(name: string): string {\n return name\n .toLowerCase()\n .normalize(\"NFKD\")\n // Strip combining marks so \"Caf\u00E9\" becomes \"cafe\", not \"caf\".\n .replace(/[\u0300-\u036F]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 63)\n // Slicing can leave a trailing hyphen, which is not a valid DNS label.\n .replace(/-+$/g, \"\");\n}\n", "/**\n * Fargate task sizing.\n *\n * Every app used to run at 256 CPU / 512 MB, hardcoded in `ecs-app.ts`. That is\n * below the working set of a stock Next.js server, so those apps were killed by\n * the OOM killer -- which surfaces as a container that exits without a message,\n * indistinguishable from a crash in the app's own code. Sizing is therefore not\n * a tuning knob; at the old default some runtimes could not work at all.\n *\n * Fargate accepts only specific (cpu, memory) pairs and rejects everything else\n * at RegisterTaskDefinition time. Validating here rather than there means the\n * caller is told which values are legal instead of reading an AWS error, and the\n * CLI can reject a bad `--memory` before a build starts.\n */\n\nimport type { Runtime } from \"./detect.js\";\n\n/**\n * Legal memory values (MiB) for each CPU unit, from the Fargate task-size table.\n *\n * Expressed as a range plus a step rather than a literal list because the larger\n * tiers have hundreds of legal values; a list would be unreadable and would drift.\n */\nconst FARGATE_SIZES: Record<number, { min: number; max: number; step: number }> = {\n 256: { min: 512, max: 2048, step: 512 },\n 512: { min: 1024, max: 4096, step: 1024 },\n 1024: { min: 2048, max: 8192, step: 1024 },\n 2048: { min: 4096, max: 16384, step: 1024 },\n 4096: { min: 8192, max: 30720, step: 1024 },\n 8192: { min: 16384, max: 61440, step: 4096 },\n 16384: { min: 32768, max: 122880, step: 8192 },\n};\n\nexport const FARGATE_CPUS = Object.keys(FARGATE_SIZES)\n .map(Number)\n .sort((a, b) => a - b);\n\n/**\n * 256 CPU is a special case: its legal memory values are 512, 1024 and 2048 --\n * it skips 1536, so the uniform step above would admit a value Fargate rejects.\n */\nconst CPU_256_MEMORY = [512, 1024, 2048];\n\nexport interface SizeValidation {\n ok: boolean;\n /** Why the pair is illegal, naming the legal alternatives. */\n error?: string;\n /** The legal memory values for the given CPU, for building an error message. */\n allowed?: number[];\n}\n\nfunction legalMemory(cpu: number): number[] {\n if (cpu === 256) return CPU_256_MEMORY;\n const spec = FARGATE_SIZES[cpu];\n if (!spec) return [];\n const values: number[] = [];\n for (let m = spec.min; m <= spec.max; m += spec.step) values.push(m);\n return values;\n}\n\n/**\n * Checks a (cpu, memory) pair against the Fargate table.\n *\n * Deliberately strict rather than snapping to the nearest legal value: silently\n * giving an app 2 GB when its owner asked for 1.5 GB means the bill and the\n * behaviour do not match what was requested, and a size that is quietly changed\n * is a size nobody can reason about when the app OOMs anyway.\n */\nexport function validateSize(cpu: number, memory: number): SizeValidation {\n if (!FARGATE_SIZES[cpu]) {\n return {\n ok: false,\n error: `cpu must be one of ${FARGATE_CPUS.join(\", \")} (got ${cpu})`,\n };\n }\n const allowed = legalMemory(cpu);\n if (!allowed.includes(memory)) {\n // Long lists are unhelpful in an error message; show the shape instead.\n const shown =\n allowed.length <= 6\n ? allowed.join(\", \")\n : `${allowed[0]}\u2013${allowed[allowed.length - 1]} in steps of ${allowed[1] - allowed[0]}`;\n return {\n ok: false,\n error: `memory ${memory} is not valid for cpu ${cpu}; allowed: ${shown}`,\n allowed,\n };\n }\n return { ok: true };\n}\n\nexport interface TaskSize {\n cpu: number;\n memory: number;\n}\n\n/**\n * The size an app gets when nobody chose one.\n *\n * Picked per runtime because the failure is runtime-specific: `next start` holds\n * a compiled server plus its module graph and does not fit in 512 MB, while a\n * Go binary is comfortable in it. Defaulting everything to the larger size would\n * quadruple the bill for the runtimes that do not need it; defaulting everything\n * to the smaller one is the bug this replaces.\n */\nexport function defaultSizeFor(runtime: Runtime): TaskSize {\n switch (runtime) {\n case \"nextjs\":\n return { cpu: 512, memory: 1024 };\n case \"nodejs\":\n case \"python\":\n // Comfortable for a framework plus dependencies, and the smallest legal\n // pair above the old default.\n return { cpu: 256, memory: 1024 };\n case \"go\":\n return { cpu: 256, memory: 512 };\n case \"custom\":\n // Unknown contents, so assume it is doing something real.\n return { cpu: 512, memory: 1024 };\n }\n}\n\n/**\n * How many tasks a size change or a scale-out may run at once.\n *\n * A bound exists because `instances` is caller-supplied and reaches RunTask\n * directly: a typo of 100 would launch a hundred Fargate tasks and bill for them\n * before anyone noticed. RunTask itself caps a single call at 10.\n */\nexport const MAX_INSTANCES = 10;\n", "/**\n * What a plan allows and what usage costs.\n *\n * Kept here rather than in the control API because three consumers need the same\n * numbers and must not disagree about them: the API enforces the limits, the\n * usage collector prices the metrics it records, and the CLI prints a bill. A\n * rate duplicated in two of those places is a bill that does not match the\n * invoice.\n *\n * Two things this deliberately is *not*:\n *\n * - **A payment integration.** Nothing here charges anyone. It computes what an\n * org owes; collecting it needs a payment processor and a human to accept its\n * terms, the same way SES production access does.\n * - **The authority on AWS cost.** These are the platform's *prices*. The real\n * cost of a Fargate task is in Cost Explorer, and the two will not match --\n * that difference is the margin, and pretending otherwise would make every\n * price change look like a metering bug.\n */\n\n/**\n * Metrics recorded in `usage_records`.\n *\n * `vcpu_seconds` and `gb_seconds` are what Fargate actually charges for, so they\n * are what a compute line is priced from; `task_seconds` is recorded alongside\n * them because it is the number a human recognises (\"this ran for 20 hours\") and\n * cannot be recovered from the other two without knowing the task size.\n *\n * `storage_mb`, not bytes: `usage_records.quantity` is an `integer`, and bytes\n * overflows int32 at 2.1 GB -- silently, wrapping negative, which would read as a\n * credit.\n *\n * `lambda_gb_seconds` and `lambda_invocations` are the serverless compute line,\n * deliberately *not* folded into `gb_seconds`. They price differently, they come\n * from a different source (CloudWatch's own measurement rather than arithmetic\n * over a deployment row), and an app that switched modes mid-month has to produce\n * an invoice a person can audit -- two lines that each say where they came from,\n * not one line that silently mixes two meters.\n */\nexport type UsageMetric =\n | \"task_seconds\"\n | \"vcpu_seconds\"\n | \"gb_seconds\"\n | \"requests\"\n | \"build_seconds\"\n | \"storage_mb\"\n | \"emails_sent\"\n | \"db_days\"\n | \"lambda_gb_seconds\"\n | \"lambda_invocations\"\n | \"egress_mb\";\n\nexport const USAGE_METRICS: UsageMetric[] = [\n \"task_seconds\",\n \"vcpu_seconds\",\n \"gb_seconds\",\n \"requests\",\n \"build_seconds\",\n \"storage_mb\",\n \"emails_sent\",\n \"db_days\",\n \"lambda_gb_seconds\",\n \"lambda_invocations\",\n \"egress_mb\",\n];\n\n/**\n * Metrics that are a level rather than a total.\n *\n * A day's `storage_mb` row is the last sample taken that day, not a sum of\n * samples -- summing a level double-counts every byte for every time it was\n * observed. Billing a level means averaging the samples across the period, which\n * is why the two kinds cannot be aggregated by the same code path.\n */\nexport const GAUGE_METRICS: UsageMetric[] = [\"storage_mb\"];\n\nexport function isGauge(metric: string): boolean {\n return (GAUGE_METRICS as string[]).includes(metric);\n}\n\n/**\n * Price per unit, in cents. Fractional on purpose: a request costs a fifth of a\n * millionth of a cent and rounding each one to a cent would bill $10,000 for a\n * million requests.\n *\n * `task_seconds` is priced at zero because compute is already billed through\n * `vcpu_seconds` and `gb_seconds`. It is in the table rather than absent from it\n * so that a new metric added without a price is a visible zero here instead of an\n * `undefined` multiplied into `NaN` further down.\n */\nexport const RATES: Record<UsageMetric, number> = {\n task_seconds: 0,\n // Fargate on-demand in us-east-1 is ~$0.04048/vCPU-hour and ~$0.004445/GB-hour.\n vcpu_seconds: 0.002,\n gb_seconds: 0.00025,\n // $0.20 per million requests.\n requests: 0.00002,\n // ~$0.012 per build-minute.\n build_seconds: 0.02,\n // $0.05 per GB-month, charged per MB-month.\n storage_mb: 0.005,\n // $0.50 per thousand.\n emails_sent: 0.05,\n // ~$3/month for a managed schema, charged per day it exists.\n db_days: 10,\n // Lambda list price is $0.0000166667 per GB-second; this carries roughly the\n // same markup over list as the Fargate rates above, so the two compute lines\n // are comparable and a mode switch does not look like a price change.\n lambda_gb_seconds: 0.0033,\n // Lambda's own $0.20 per million invocations, passed through at list. The\n // platform's markup on a request is already collected once by the `requests`\n // line at the edge, and one visit is one event -- marking it up on both meters\n // would charge twice for the same request.\n lambda_invocations: 0.00002,\n // Metered but not yet billed. AWS charges ~$0.09/GB out of the ALB and the\n // platform currently absorbs it, which is fine at guestbook scale and a real\n // margin leak the day someone serves video. Recorded now so the exposure is\n // a number on a dashboard instead of a surprise in Cost Explorer -- and so\n // that starting to charge for it is a rate change with history behind it,\n // not a new meter nobody was warned about. (MB, not bytes: `quantity` is an\n // int32 and bytes wraps negative at 2.1 GB, which would read as a credit.)\n egress_mb: 0,\n};\n\n/** Human unit for each metric, for a bill a person has to read. */\nexport const METRIC_LABELS: Record<UsageMetric, string> = {\n task_seconds: \"Task-seconds served\",\n vcpu_seconds: \"vCPU-seconds\",\n gb_seconds: \"GB-seconds\",\n requests: \"Requests\",\n build_seconds: \"Build seconds\",\n storage_mb: \"Object storage (MB-month)\",\n emails_sent: \"Emails sent\",\n db_days: \"Managed Postgres (app-days)\",\n lambda_gb_seconds: \"Serverless GB-seconds\",\n lambda_invocations: \"Serverless invocations\",\n egress_mb: \"Bandwidth out (MB)\",\n};\n\nexport interface Plan {\n id: string;\n name: string;\n /** Monthly subscription, in cents, before usage. */\n baseCents: number;\n /**\n * Usage covered by the base fee, in cents. An org that stays under this pays\n * exactly `baseCents` -- so the free plan is a real free tier rather than a\n * $0.03 invoice for a weekend project.\n */\n includedUsageCents: number;\n /** Apps that are not soft-deleted. */\n maxApps: number;\n maxInstancesPerApp: number;\n /** Ceilings on Fargate task size, which is the one knob that scales the bill. */\n maxCpu: number;\n maxMemory: number;\n /** Object-storage quota, enforced on upload. */\n maxStorageMb: number;\n postgres: boolean;\n customDomains: boolean;\n}\n\n/**\n * The plans.\n *\n * The limits that exist are the ones that bound *spend*: app count, instance\n * count and task size. There is deliberately no limit on requests, builds or\n * deploys -- an agent-built app that gets popular should get a bill, not an\n * outage, and a platform whose selling point is \"push and it is live\" cannot\n * answer a deploy with \"not on your plan\".\n */\nexport const PLANS: Record<string, Plan> = {\n free: {\n id: \"free\",\n name: \"Free\",\n baseCents: 0,\n includedUsageCents: 300,\n maxApps: 3,\n maxInstancesPerApp: 1,\n // A *ceiling*, not a promise of free compute: a container task at even\n // 256/512 running all month is ~$16 of usage against the $3 allowance.\n // That is why apps created on this plan default to serverless mode, which\n // meters nothing while idle -- the free tier is \"free while it sleeps\",\n // and a container app on this plan is a deliberate, metered choice.\n maxCpu: 512,\n maxMemory: 1024,\n maxStorageMb: 1024,\n postgres: true,\n customDomains: false,\n },\n pro: {\n id: \"pro\",\n name: \"Pro\",\n baseCents: 2000,\n includedUsageCents: 2000,\n maxApps: 50,\n maxInstancesPerApp: 4,\n maxCpu: 4096,\n maxMemory: 8192,\n maxStorageMb: 100 * 1024,\n postgres: true,\n customDomains: true,\n },\n scale: {\n id: \"scale\",\n name: \"Scale\",\n baseCents: 20000,\n includedUsageCents: 20000,\n maxApps: 500,\n maxInstancesPerApp: 10,\n maxCpu: 16384,\n maxMemory: 122880,\n maxStorageMb: 1024 * 1024,\n postgres: true,\n customDomains: true,\n },\n};\n\n/**\n * The plan for a stored `orgs.plan` value.\n *\n * Falls back to free rather than throwing: the column is free text and an\n * unrecognised value must not take an org's whole control surface down with a\n * 500. Free is the safe direction -- its limits are the tightest, so a typo\n * cannot silently grant a larger allowance than anyone agreed to.\n */\nexport function planFor(plan: string | null | undefined): Plan {\n return PLANS[plan || \"free\"] ?? PLANS.free;\n}\n\nexport interface UsageLine {\n metric: UsageMetric | string;\n label: string;\n quantity: number;\n unitPriceCents: number;\n /** Rounded to whole cents only at the line, never per unit. */\n cents: number;\n}\n\nexport interface Invoice {\n plan: string;\n periodStart: string;\n periodEnd: string;\n lines: UsageLine[];\n /** Everything the usage lines add up to, before the included allowance. */\n usageCents: number;\n baseCents: number;\n includedUsageCents: number;\n /** Usage beyond the allowance. Zero, never negative: credit does not roll over. */\n overageCents: number;\n totalCents: number;\n}\n\n/**\n * Prices a period's usage.\n *\n * `quantities` is metric \u2192 quantity, already aggregated over the period (sums for\n * counters, averages for gauges -- see `GAUGE_METRICS`, and note that this\n * function cannot tell which it was given, so aggregating wrongly produces a\n * plausible bill rather than an error).\n */\nexport function priceUsage(\n quantities: Record<string, number>,\n plan: Plan,\n period: { start: Date; end: Date },\n): Invoice {\n const lines: UsageLine[] = [];\n for (const metric of USAGE_METRICS) {\n const quantity = quantities[metric] ?? 0;\n const rate = RATES[metric];\n // A zero-priced metric is still reported when there is something to report:\n // task-seconds is how an owner checks that the compute line is plausible.\n if (quantity === 0) continue;\n lines.push({\n metric,\n label: METRIC_LABELS[metric],\n quantity: Math.round(quantity * 100) / 100,\n unitPriceCents: rate,\n cents: Math.round(quantity * rate),\n });\n }\n // Metrics recorded by a newer collector than this pricing table knows about.\n // Listed at zero rather than dropped: an unpriced metric that vanishes from the\n // bill is indistinguishable from one that was never collected.\n for (const metric of Object.keys(quantities)) {\n if ((USAGE_METRICS as string[]).includes(metric)) continue;\n if (!quantities[metric]) continue;\n lines.push({\n metric,\n label: metric,\n quantity: quantities[metric],\n unitPriceCents: 0,\n cents: 0,\n });\n }\n\n const usageCents = lines.reduce((acc, l) => acc + l.cents, 0);\n const overageCents = Math.max(0, usageCents - plan.includedUsageCents);\n return {\n plan: plan.id,\n periodStart: period.start.toISOString(),\n periodEnd: period.end.toISOString(),\n lines,\n usageCents,\n baseCents: plan.baseCents,\n includedUsageCents: plan.includedUsageCents,\n overageCents,\n totalCents: plan.baseCents + overageCents,\n };\n}\n\n/** `1234` \u2192 `\"$12.34\"`. For output a person reads, not for arithmetic. */\nexport function formatCents(cents: number): string {\n const sign = cents < 0 ? \"-\" : \"\";\n const abs = Math.abs(Math.round(cents));\n return `${sign}$${Math.floor(abs / 100)}.${String(abs % 100).padStart(2, \"0\")}`;\n}\n\nexport interface LimitCheck {\n ok: boolean;\n /** Names the limit and the plan, so the error says what to do about it. */\n error?: string;\n code?: string;\n}\n\nexport function allowed(): LimitCheck {\n return { ok: true };\n}\n\nfunction denied(code: string, error: string): LimitCheck {\n return { ok: false, error, code };\n}\n\n/** Whether one more app is within the plan. `current` excludes deleted apps. */\nexport function checkAppCount(plan: Plan, current: number): LimitCheck {\n if (current < plan.maxApps) return allowed();\n return denied(\n \"PLAN_LIMIT_APPS\",\n `The ${plan.name} plan allows ${plan.maxApps} app${plan.maxApps === 1 ? \"\" : \"s\"} ` +\n `and this org has ${current}. Delete one or upgrade the plan.`,\n );\n}\n\n/**\n * Whether a requested task shape is within the plan.\n *\n * Checked as a triple rather than three separate limits because they multiply:\n * the thing being bounded is the bill, and 4 instances of a small task costs the\n * same as one large one.\n */\nexport function checkTaskShape(\n plan: Plan,\n shape: { cpu?: number | null; memory?: number | null; instances?: number | null },\n): LimitCheck {\n if (shape.instances != null && shape.instances > plan.maxInstancesPerApp) {\n return denied(\n \"PLAN_LIMIT_INSTANCES\",\n `The ${plan.name} plan allows ${plan.maxInstancesPerApp} instance` +\n `${plan.maxInstancesPerApp === 1 ? \"\" : \"s\"} per app (asked for ${shape.instances}).`,\n );\n }\n if (shape.cpu != null && shape.cpu > plan.maxCpu) {\n return denied(\n \"PLAN_LIMIT_CPU\",\n `The ${plan.name} plan allows up to ${plan.maxCpu} CPU units per task (asked for ${shape.cpu}).`,\n );\n }\n if (shape.memory != null && shape.memory > plan.maxMemory) {\n return denied(\n \"PLAN_LIMIT_MEMORY\",\n `The ${plan.name} plan allows up to ${plan.maxMemory} MB per task (asked for ${shape.memory}).`,\n );\n }\n return allowed();\n}\n\n/** Whether a custom domain is within the plan. */\nexport function checkCustomDomain(plan: Plan): LimitCheck {\n if (plan.customDomains) return allowed();\n return denied(\n \"PLAN_LIMIT_DOMAINS\",\n `Custom domains are not included in the ${plan.name} plan. Every app keeps its ` +\n `platform hostname, which needs no configuration.`,\n );\n}\n", "/**\n * Who may reach a running app, as a ladder from narrowest to widest.\n *\n * This replaces `auth_required`, which could only distinguish \"gated\" from\n * \"open\" and so collapsed the two settings owners actually reach for -- \"anyone\n * with the link\" and \"only the people I invite\" -- into the same switch as\n * \"anyone in my org\".\n *\n * The tiers are deliberately ordered and each is a strict superset of the one\n * before it, so widening is always one step and there is never a pair of\n * settings whose relative reach is ambiguous.\n *\n * Note what this is *not*: it governs visiting the deployed app at its\n * hostname, not managing it through the API. Those are separate on purpose --\n * `lib/access.ts` answers the second one -- so an org member who cannot open a\n * `private` app in a browser may still hold write on it in the console. The\n * alternative, letting a visibility setting revoke an org member's ability to\n * deploy, would make an access control into an outage.\n */\nexport const VISIBILITIES = [\"private\", \"org\", \"authenticated\", \"public\"] as const;\n\nexport type Visibility = (typeof VISIBILITIES)[number];\n\n/**\n * The default for a new app, and the widest tier that still requires a session.\n *\n * Matches what `auth_required` defaulted to (true, meaning org-or-share), so\n * creating an app has not changed who can reach it.\n */\nexport const DEFAULT_VISIBILITY: Visibility = \"org\";\n\nexport function isVisibility(value: unknown): value is Visibility {\n return typeof value === \"string\" && (VISIBILITIES as readonly string[]).includes(value);\n}\n\n/**\n * Reads a stored value as a tier, treating anything unrecognized as the\n * **narrowest** one.\n *\n * The column is `text`, and this repo has twice shipped a value nothing reads\n * (`org_members.role = 'owner'`, `deployments.status = 'rolled_back'`) -- both\n * stored happily and were silently ignored. Here the same slip would decide\n * whether strangers can read a tenant's app, so an unknown value must fail\n * closed. `private` is still reachable by its org's admins, so the worst case of\n * failing closed is an app its owners can fix; the worst case of failing open is\n * a disclosure they cannot undo.\n */\nexport function visibilityOf(value: unknown): Visibility {\n return isVisibility(value) ? value : \"private\";\n}\n\n/**\n * Whether a tier admits a visitor with no session at all.\n *\n * This is what the legacy `auth_required` boolean encodes, which is why every\n * write to `visibility` also writes `auth_required = !isPublic(v)`: the deployed\n * edge router may still be reading only the boolean.\n */\nexport function isPublic(v: Visibility): boolean {\n return v === \"public\";\n}\n\n/**\n * Human-readable label, used by the CLI. The console deliberately imports no\n * workspace package, so its copy of these lives in\n * `apps/console-v2/lib/api.ts` (`VISIBILITY`) \u2014 keep the wording identical, or\n * the same tier reads differently in the two places a person sees it.\n */\nexport const VISIBILITY_LABELS: Record<Visibility, string> = {\n private: \"Only people with access\",\n org: \"Anyone in your organization\",\n authenticated: \"Anyone signed in to Videan\",\n public: \"Anyone with the link\",\n};\n", "import Conf from \"conf\";\n\ninterface CloudConfig {\n apiKey?: string;\n defaultOrg?: string;\n currentApp?: string;\n baseUrl?: string;\n}\n\nconst config = new Conf<CloudConfig>({\n projectName: \"cloud-cli\",\n schema: {\n apiKey: { type: \"string\" },\n defaultOrg: { type: \"string\" },\n currentApp: { type: \"string\" },\n baseUrl: { type: \"string\" },\n },\n});\n\n/**\n * An override from the environment, checked under both prefixes.\n *\n * The bin is `videan`; the packages are `@cloud/*`. Accepting both spellings is\n * cheaper than being wrong about which one someone reaches for.\n */\nfunction fromEnv(name: string): string | undefined {\n const value = process.env[`VIDEAN_${name}`] || process.env[`CLOUD_${name}`];\n return value ? value : undefined;\n}\n\n/**\n * The environment wins over the saved config.\n *\n * The platform's premise is that an agent does the deploying, and an agent runs\n * in a non-interactive shell with no config file and no way to answer a password\n * prompt. `VIDEAN_API_KEY` is how it authenticates without `login` at all.\n */\nexport function getApiKey(): string | undefined {\n return fromEnv(\"API_KEY\") || config.get(\"apiKey\");\n}\n\nexport function setApiKey(key: string): void {\n config.set(\"apiKey\", key);\n}\n\nexport function getDefaultOrg(): string | undefined {\n // Both spellings again: the README documents VIDEAN_ORG_ID (the value *is*\n // an id), and the first real user set exactly that while the code read only\n // VIDEAN_ORG \u2014 so the CLI silently fell back to a stale saved org and\n // answered \"Not a member of this organization\" for a key that was fine.\n return fromEnv(\"ORG_ID\") || fromEnv(\"ORG\") || config.get(\"defaultOrg\");\n}\n\nexport function setDefaultOrg(org: string): void {\n config.set(\"defaultOrg\", org);\n}\n\nexport function getCurrentApp(): string | undefined {\n return fromEnv(\"APP_ID\") || fromEnv(\"APP\") || config.get(\"currentApp\");\n}\n\nexport function setCurrentApp(appId: string): void {\n config.set(\"currentApp\", appId);\n}\n\nexport function getBaseUrl(): string | undefined {\n return fromEnv(\"API_URL\") || config.get(\"baseUrl\");\n}\n\nexport function setBaseUrl(url: string): void {\n config.set(\"baseUrl\", url);\n}\n\n/** Where the saved config lives, so a stale value can be found and inspected. */\nexport function configPath(): string {\n return config.path;\n}\n\nexport function clearConfig(): void {\n config.clear();\n}\n", "import inquirer from \"inquirer\";\nimport chalk from \"chalk\";\n\n/**\n * The one gate every interactive prompt goes through. Agents, CI jobs and\n * scripts run this CLI on closed or piped stdin, where legacy inquirer\n * renders the prompt, then dies uncaught (ERR_USE_AFTER_CLOSE from its\n * force-close handler) when the stream ends. So: refuse up front on a\n * non-TTY with a message naming the flag alternative, and treat a stream\n * that closes mid-prompt (a PTY torn down by a dying harness passes the\n * isTTY check) as the same refusal rather than a crash.\n */\nexport async function prompt<T = Record<string, unknown>>(\n questions: Parameters<typeof inquirer.prompt>[0],\n nonInteractiveHint: string,\n): Promise<T> {\n if (!process.stdin.isTTY) {\n console.error(\n chalk.red(`This command needs a terminal to prompt. ${nonInteractiveHint}`),\n );\n process.exit(1);\n }\n try {\n return (await inquirer.prompt(questions)) as T;\n } catch (err) {\n if (isStreamClosed(err)) {\n console.error(chalk.red(`Input ended before the prompt was answered. ${nonInteractiveHint}`));\n process.exit(1);\n }\n throw err;\n }\n}\n\nfunction isStreamClosed(err: unknown): boolean {\n const e = err as { code?: string; message?: string } | undefined;\n return e?.code === \"ERR_USE_AFTER_CLOSE\" || /closed/i.test(e?.message ?? \"\");\n}\n\n/**\n * Inquirer's force-close handler runs from a readline \"close\" event, outside\n * any promise chain, so when a PTY dies mid-prompt the ERR_USE_AFTER_CLOSE it\n * throws is uncatchable at the call site \u2014 only here. Everything else still\n * crashes loudly.\n */\nexport function installPromptCrashGuard(): void {\n process.on(\"uncaughtException\", (err) => {\n if (isStreamClosed(err)) {\n console.error(\n chalk.red(\n \"Input stream closed mid-prompt. Re-run with the answers as flags \u2014 every prompt has one (see --help).\",\n ),\n );\n process.exit(1);\n }\n throw err;\n });\n}\n", "import http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport { spawn } from \"node:child_process\";\n\n/**\n * The loopback half of `videan login --browser`.\n *\n * The CLI listens on an ephemeral 127.0.0.1 port and opens the console's\n * /cli-auth page with that port and a random state. The page has the user\n * sign in (password, Google or SSO \u2014 whatever their account uses) and click\n * Authorize, which top-level-navigates back to\n * `http://127.0.0.1:<port>/callback?state=\u2026&token=\u2026`. The state must match or\n * the delivery is refused: without it, any page could push a token of the\n * attacker's choosing into the waiting CLI (login CSRF).\n *\n * The token never leaves the machine \u2014 the console page navigates to the\n * loopback interface, and the fragment-vs-query concern from the Google flow\n * does not apply (a fragment never reaches an HTTP server, which here is the\n * whole point of the query string).\n */\n\nexport interface CallbackServer {\n port: number;\n /** Resolves with the token, or rejects on deny/timeout/mismatch. */\n waitForToken: Promise<string>;\n close: () => void;\n}\n\nconst LANDING = (title: string, sub: string) => `<!doctype html>\n<meta charset=\"utf-8\"><title>${title}</title>\n<body style=\"font-family:system-ui;background:#101014;color:#e8e8ea;display:grid;place-items:center;min-height:90vh\">\n<div style=\"text-align:center\"><h1 style=\"font-weight:600\">${title}</h1><p style=\"color:#9a9aa2\">${sub}</p></div>`;\n\nexport function createCallbackServer(\n state: string,\n timeoutMs = 5 * 60 * 1000,\n redirectOrigin?: string,\n): Promise<CallbackServer> {\n return new Promise((resolveServer, rejectServer) => {\n let settle: { resolve: (t: string) => void; reject: (e: Error) => void };\n const waitForToken = new Promise<string>((resolve, reject) => {\n settle = { resolve, reject };\n });\n // The browser can deliver (or deny) before the caller has awaited; without\n // a standing handler that instant is an \"unhandled rejection\" warning. This\n // side chain absorbs it \u2014 the caller's own await still sees the rejection.\n waitForToken.catch(() => {});\n\n const server = http.createServer((req, res) => {\n const url = new URL(req.url || \"/\", \"http://127.0.0.1\");\n if (url.pathname !== \"/callback\") {\n res.writeHead(404).end();\n return;\n }\n // Wrong state: answer the request but keep waiting \u2014 refusing to settle\n // means a spray of forged deliveries cannot end the login early.\n if (url.searchParams.get(\"state\") !== state) {\n res.writeHead(403, { \"Content-Type\": \"text/html\" });\n res.end(LANDING(\"Request refused\", \"This authorization was not started by this CLI.\"));\n return;\n }\n // Ending on videan.xyz beats ending on a raw 127.0.0.1 URL, so when the\n // caller knows the console origin the outcome pages redirect back to it.\n // Only the exact-state branches redirect \u2014 a forged delivery gets local\n // HTML, never a bounce of the attacker's choosing.\n const land = (done: string, status: number, title: string, sub: string) => {\n if (redirectOrigin) {\n res.writeHead(302, { Location: `${redirectOrigin}/cli-auth?done=${done}` });\n res.end();\n } else {\n res.writeHead(status, { \"Content-Type\": \"text/html\" });\n res.end(LANDING(title, sub));\n }\n };\n if (url.searchParams.get(\"error\")) {\n land(\"denied\", 200, \"Login cancelled\", \"You can close this tab.\");\n finish(() => settle.reject(new Error(\"Authorization was declined in the browser.\")));\n return;\n }\n const token = url.searchParams.get(\"token\");\n if (!token) {\n res.writeHead(400, { \"Content-Type\": \"text/html\" });\n res.end(LANDING(\"Something went wrong\", \"No token arrived. Run `videan login` again.\"));\n finish(() => settle.reject(new Error(\"The browser sent no token.\")));\n return;\n }\n land(\"ok\", 200, \"You're logged in\", \"Return to your terminal \u2014 you can close this tab.\");\n finish(() => settle.resolve(token));\n });\n\n const timer = setTimeout(() => {\n finish(() => settle.reject(new Error(\"Timed out waiting for the browser (5 minutes).\")));\n }, timeoutMs);\n // A pending timer or server must not hold the process open after main ends.\n timer.unref();\n\n let done = false;\n function finish(outcome: () => void) {\n if (done) return;\n done = true;\n clearTimeout(timer);\n outcome();\n // Give the final response a beat to flush before tearing down.\n setTimeout(() => server.close(), 100).unref();\n }\n\n server.on(\"error\", (e) => rejectServer(e));\n server.listen(0, \"127.0.0.1\", () => {\n const address = server.address();\n if (!address || typeof address === \"string\") {\n rejectServer(new Error(\"Could not bind a loopback port.\"));\n return;\n }\n resolveServer({\n port: address.port,\n waitForToken,\n close: () => finish(() => settle.reject(new Error(\"closed\"))),\n });\n });\n });\n}\n\nexport function randomState(): string {\n return crypto.randomBytes(16).toString(\"hex\");\n}\n\n/**\n * The console origin that pairs with an API base URL: `api.<domain>/v1` \u2192\n * `https://<domain>`. Anything not shaped that way has no console we can\n * guess, and the caller falls back to the password prompt with an explanation.\n */\nexport function consoleOriginFor(baseUrl: string): string | null {\n try {\n const url = new URL(baseUrl);\n if (url.protocol !== \"https:\" || !url.hostname.startsWith(\"api.\")) return null;\n return `https://${url.hostname.slice(4)}`;\n } catch {\n return null;\n }\n}\n\n/** Best-effort `open <url>`; the URL is always printed too, so failure is fine. */\nexport function openBrowser(url: string): void {\n const [cmd, args] =\n process.platform === \"darwin\"\n ? [\"open\", [url]]\n : process.platform === \"win32\"\n ? [\"cmd\", [\"/c\", \"start\", \"\", url]]\n : [\"xdg-open\", [url]];\n try {\n spawn(cmd, args as string[], { stdio: \"ignore\", detached: true }).unref();\n } catch {\n // Printed URL is the fallback.\n }\n}\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { clearConfig, configPath } from \"../config.js\";\n\nexport const logoutCommand = new Command(\"logout\")\n .description(\"Forget the saved key, org, app and API URL\")\n .action(() => {\n // Clears everything, not just the key. The saved `baseUrl` is the value most\n // likely to be wrong and was, until now, only reachable by editing the\n // config file: a leftover URL from an earlier environment made every command\n // fail against a host that no longer served the API.\n const where = configPath();\n clearConfig();\n console.log(chalk.green(\"Logged out.\"));\n console.log(chalk.gray(` Cleared ${where}`));\n });\n", "import { Command } from \"commander\";\nimport inquirer from \"inquirer\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { SUPPORTED_RUNTIMES, CONFIG_FILE_NAME } from \"@cloud/shared\";\nimport { writeVideanMd } from \"../videan-md.js\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Initialize a new Cloud app in the current directory\")\n .option(\"--name <name>\", \"App name (defaults to the directory name)\")\n .option(\"--runtime <runtime>\", \"One of: \" + (SUPPORTED_RUNTIMES as unknown as string[]).join(\", \"))\n .option(\"--entrypoint <file>\", \"Entry file\", \"index.ts\")\n .option(\"--yes\", \"Take the defaults without prompting\")\n .action(async (opts) => {\n const configPath = path.join(process.cwd(), CONFIG_FILE_NAME);\n // Agents run in non-interactive shells; a prompt there is a hang. Flags or\n // defaults cover everything, and the config file is optional anyway \u2014 the\n // platform detects runtime and entrypoint from the pushed files.\n const interactive = process.stdin.isTTY && !opts.yes;\n\n if (await fs.pathExists(configPath)) {\n if (!interactive) {\n console.log(chalk.yellow(`${CONFIG_FILE_NAME} already exists; leaving it alone.`));\n } else {\n console.log(chalk.yellow(`${CONFIG_FILE_NAME} already exists in this directory.`));\n const { overwrite } = await inquirer.prompt([\n { type: \"confirm\", name: \"overwrite\", message: \"Overwrite?\", default: false },\n ]);\n if (!overwrite) {\n // Still drop the agent contract \u2014 that half of init is idempotent and\n // the reason a repeat run is worth anything.\n for (const f of await writeVideanMd(process.cwd())) console.log(chalk.green(`Wrote ${f}`));\n return;\n }\n }\n }\n\n if (!(await fs.pathExists(configPath)) || interactive) {\n const answers = interactive\n ? await inquirer.prompt([\n { type: \"input\", name: \"name\", message: \"App name:\", default: opts.name || path.basename(process.cwd()) },\n {\n type: \"list\",\n name: \"runtime\",\n message: \"Runtime:\",\n choices: SUPPORTED_RUNTIMES as unknown as string[],\n },\n { type: \"input\", name: \"entrypoint\", message: \"Entrypoint:\", default: opts.entrypoint },\n ])\n : {\n name: opts.name || path.basename(process.cwd()),\n runtime: opts.runtime || (SUPPORTED_RUNTIMES as unknown as string[])[0],\n entrypoint: opts.entrypoint,\n };\n\n const config = [\n `name: ${answers.name}`,\n `runtime: ${answers.runtime}`,\n `entrypoint: ${answers.entrypoint}`,\n \"\",\n ].join(\"\\n\");\n\n await fs.writeFile(configPath, config, \"utf-8\");\n console.log(chalk.green(`Created ${CONFIG_FILE_NAME}`));\n }\n\n // The part an agent actually needs: the platform contract, in the repo,\n // where the next model session will read it unprompted.\n for (const f of await writeVideanMd(process.cwd())) console.log(chalk.green(`Wrote ${f}`));\n });\n", "import fs from \"fs-extra\";\nimport path from \"node:path\";\n\n/**\n * Drops the platform contract into the project directory, where an agent's\n * eyes actually are.\n *\n * A README on npm is where instructions go to be technically published; the\n * repo an agent is editing is the one context it reliably reads. `videan init`\n * writes VIDEAN.md and appends a pointer to CLAUDE.md / AGENTS.md when those\n * already exist (never creating them \u2014 that would be claiming a file the\n * project's own conventions own). The single most important line in it is the\n * auth rule: without it, agents generate a login page for gated apps and every\n * visitor signs in twice.\n */\n\nexport const VIDEAN_MD = `# This project deploys to Videan\n\nDeploy: \\`videan deploy\\` (from this directory). Logs: \\`videan logs -f\\`.\nUndo a bad deploy: \\`videan rollback\\`. Full agent guide: https://videan.xyz/llms.txt\n\nThe contract: serve HTTP on **port 3000**. Runtime (Node/Python/Next.js/Go/\nDockerfile) is detected from the files \u2014 no config needed. If the app has a\nmanaged database, read \\`DATABASE_URL\\` from the environment. Log to stdout.\n\n## Auth \u2014 read this before writing any login code\n\nThis platform puts a sign-in gate in front of apps unless they are public.\n\n- **Gated app (visibility private/org/authenticated \u2014 the default): DO NOT\n build a login page.** Every request arrives with the visitor already\n authenticated: read \\`X-User-Id\\`, \\`X-User-Email\\`, \\`X-User-Roles\\`. If the\n app needs its own user records, find-or-create a local user from\n \\`X-User-Email\\` and start your session from that \u2014 never render a login\n form when the header is present.\n- **Public app** (\\`videan apps set --public\\`): the gate is out of the path,\n no headers arrive, bring your own auth if the app needs any. Required for\n anything that must receive webhooks from external services.\n\nSecrets: \\`videan secrets set KEY=value\\` (UPPERCASE keys) \u2014 injected as env\nvars on the next deploy. Never commit credentials to this repo instead.\n\n## Execution modes\n\nApps run as a **container** (default: always on, no cold starts) or\n**serverless** (scales to zero, cold start after idle \u2014 good for internal\ntools with sporadic traffic). Set at create (\\`videan apps create --mode\nserverless\\`) or later (\\`videan apps set --mode \u2026\\`, applied by the next\ndeploy). Serverless refuses cron, background jobs, WebSockets and custom\nDockerfiles; container mode supports all of them.\n`;\n\nconst POINTER =\n \"\\n## Videan\\n\\nThis project deploys to Videan \u2014 see VIDEAN.md for the platform contract\" +\n \" (port 3000, auth headers, deploy commands) before writing server or login code.\\n\";\n\nexport async function writeVideanMd(dir: string): Promise<string[]> {\n const wrote: string[] = [];\n const target = path.join(dir, \"VIDEAN.md\");\n if (!(await fs.pathExists(target))) {\n await fs.writeFile(target, VIDEAN_MD, \"utf-8\");\n wrote.push(\"VIDEAN.md\");\n }\n for (const name of [\"CLAUDE.md\", \"AGENTS.md\"]) {\n const p = path.join(dir, name);\n if (!(await fs.pathExists(p))) continue;\n const body = await fs.readFile(p, \"utf-8\");\n if (body.includes(\"VIDEAN.md\")) continue;\n await fs.appendFile(p, POINTER, \"utf-8\");\n wrote.push(`${name} (pointer)`);\n }\n return wrote;\n}\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { collectFiles, requireApiKey, formatStatus } from \"../util.js\";\n\nexport const deployCommand = new Command(\"deploy\")\n .description(\"Deploy the current directory\")\n .option(\"-a, --app <id>\", \"App ID to deploy to\")\n .option(\"-m, --message <msg>\", \"Deployment message\")\n .option(\"--env <environment>\", \"preview or production (default: production)\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or run `videan init` first.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n\n // Collect files\n const spinner = ora(\"Collecting files...\").start();\n const { files, binaryFiles, skipped } = await collectFiles(process.cwd());\n const binaryCount = Object.keys(binaryFiles).length;\n const fileCount = Object.keys(files).length + binaryCount;\n if (!fileCount) {\n spinner.fail(chalk.red(`Nothing to deploy: no files under ${process.cwd()}.`));\n process.exit(1);\n }\n if (skipped.length) {\n // Named, not counted. A snapshot silently missing an image is a bug report\n // from the future; the operator can act on a list.\n spinner.warn(chalk.yellow(`Skipped ${skipped.length} file(s):`));\n for (const file of skipped) console.log(chalk.gray(` ${file.path} \u2014 ${file.reason}`));\n spinner.start();\n }\n spinner.text = `Pushing ${fileCount} files${binaryCount ? ` (${binaryCount} binary)` : \"\"}...`;\n\n // Push\n const snapshot = await client.apps.push(appId, files, opts.message, binaryFiles);\n spinner.text = \"Triggering deployment...\";\n\n // Deploy\n if (opts.env && opts.env !== \"preview\" && opts.env !== \"production\") {\n spinner.fail(chalk.red(`--env must be preview or production, not ${opts.env}`));\n process.exit(1);\n }\n const deployment = await client.apps.deploy(appId, snapshot.id, opts.env);\n spinner.text = `Deploying (${deployment.status})...`;\n\n // Poll for status. A build takes minutes, so a single failed request must\n // not end the watch -- it used to `break`, which reported the deployment as\n // stuck in `building` when in fact nothing was wrong with it but one HTTP\n // call. Give up only after several consecutive failures, and say so.\n let current = deployment;\n let consecutiveErrors = 0;\n let lastError = \"\";\n while (current.status === \"building\" || current.status === \"deploying\") {\n await new Promise((r) => setTimeout(r, 5000));\n const elapsed = Math.round((Date.now() - Date.parse(current.createdAt)) / 1000);\n spinner.text = `${current.status === \"building\" ? \"Building\" : \"Starting\"}... (${elapsed}s)`;\n try {\n current = await client.apps.checkDeployment(deployment.id);\n consecutiveErrors = 0;\n } catch (e: any) {\n lastError = e?.message || String(e);\n if (++consecutiveErrors >= 5) {\n spinner.fail(chalk.red(`Lost track of the deployment: ${lastError}`));\n console.log(chalk.gray(` It may still finish. Check with \\`videan status\\`.`));\n process.exit(1);\n }\n }\n }\n\n if (current.status === \"running\") {\n spinner.succeed(chalk.green(`Deployed successfully!`));\n // One line of doctrine at the exact moment an agent is reading output:\n // gated apps must not grow a login page, public apps must bring their own.\n try {\n const app = await client.apps.get(appId);\n const gated = app.visibility ? app.visibility !== \"public\" : app.authRequired !== false;\n console.log(\n gated\n ? chalk.gray(\n ` Access: ${app.visibility || \"org\"} \u2014 visitors sign in once at the edge; ` +\n `your app receives X-User-Id / X-User-Email on every request. Don't build a login page.`,\n )\n : chalk.gray(` Access: public \u2014 reachable by anyone; bring your own auth if it needs any.`),\n );\n } catch {\n /* cosmetic only \u2014 a failed lookup must not fail a finished deploy */\n }\n if (current.url) {\n // Deployments promoted before the API started storing a full URL have a\n // bare hostname here, which no terminal will linkify and no fetch will\n // follow.\n const url = /^https?:\\/\\//.test(current.url) ? current.url : `https://${current.url}`;\n console.log(chalk.cyan(` URL: ${url}`));\n }\n if (current.previewUrl) {\n // This version's own address. Live from the moment its tasks are up --\n // which is before promotion, so it is what an agent checks its own work\n // against -- and only for as long as they are: the poller retires the\n // version this deploy replaced, and a retired version's address stops\n // resolving with it.\n console.log(chalk.gray(` This version: ${current.previewUrl}`));\n }\n } else if (current.status === \"failed\" || current.status === \"crashed\") {\n // `crashed` means the container started and then died -- a failure the\n // caller has to see, and one this used to report as mere information with\n // a zero exit code, so a scripted deploy counted it as a success.\n spinner.fail(chalk.red(`Deployment ${current.status}: ${current.error || \"unknown error\"}`));\n console.log(chalk.gray(` Logs: videan logs --app ${appId}`));\n process.exit(1);\n } else {\n spinner.info(`Deployment status: ${formatStatus(current.status)}`);\n process.exit(1);\n }\n });\n", "import fs from \"fs-extra\";\nimport path from \"node:path\";\nimport ignore from \"ignore\";\nimport chalk from \"chalk\";\n\n/** Larger than this and the push is refused rather than silently trimmed. */\nexport const MAX_FILE_BYTES = 10 * 1024 * 1024;\n\nexport interface CollectedFiles {\n /** Relative path \u2192 UTF-8 contents, ready for the push wire format. */\n files: Record<string, string>;\n /** Relative path \u2192 base64 contents, for files that are not valid UTF-8. */\n binaryFiles: Record<string, string>;\n /**\n * Relative paths left out, with the reason. Only oversized files land here\n * now that binaries have somewhere to go.\n */\n skipped: { path: string; reason: string }[];\n}\n\n/**\n * Collect all files from a directory, respecting .gitignore and .cloudignore.\n */\nexport async function collectFiles(dir: string): Promise<CollectedFiles> {\n const ig = ignore();\n\n // Always ignored, whether or not the project has a .gitignore -- and many\n // agent-generated projects do not.\n //\n // `.next`, `.turbo` and `coverage` are local build output. Uploading them is\n // not merely wasteful: the container runs `next build` over whatever it is\n // given, and a `.next` produced on a different platform and Node version is\n // exactly the kind of stale cache that makes a build fail or, worse, succeed\n // with the previous code in it.\n ig.add([\n \".git\",\n \"node_modules\",\n \".env\",\n \".env.*\",\n \".next\",\n \".turbo\",\n \"coverage\",\n \".DS_Store\",\n \"*.log\",\n ]);\n\n // Load .gitignore\n const gitignorePath = path.join(dir, \".gitignore\");\n if (await fs.pathExists(gitignorePath)) {\n const content = await fs.readFile(gitignorePath, \"utf-8\");\n ig.add(content);\n }\n\n // Load .cloudignore\n const cloudignorePath = path.join(dir, \".cloudignore\");\n if (await fs.pathExists(cloudignorePath)) {\n const content = await fs.readFile(cloudignorePath, \"utf-8\");\n ig.add(content);\n }\n\n const files: Record<string, string> = {};\n const binaryFiles: Record<string, string> = {};\n const skipped: { path: string; reason: string }[] = [];\n\n async function walk(currentDir: string): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n const relativePath = path.relative(dir, fullPath);\n\n // ignore() matches directories only when the path is marked as one, and\n // pattern semantics differ between \"dir\" and \"dir/\" for anchored rules.\n const ignorePath = entry.isDirectory() ? `${relativePath}/` : relativePath;\n if (ig.ignores(ignorePath)) continue;\n\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.isFile()) {\n // Read bytes, not a string. `readFile(path, \"utf-8\")` replaces every\n // byte that is not valid UTF-8 with U+FFFD and reports success, so a\n // PNG read as text arrives corrupted with nothing to indicate it. The\n // round-trip is what decides which map a file belongs in: text goes in\n // `files` verbatim, everything else is base64 in `binaryFiles`. It is a\n // test of the bytes rather than of the extension, so an unknown\n // extension or a text file with one byte of Latin-1 in it is handled on\n // what it contains.\n const buffer = await fs.readFile(fullPath);\n if (buffer.byteLength > MAX_FILE_BYTES) {\n // Refused, not trimmed: the push is one JSON body, and a 200MB asset\n // would be an out-of-memory on the control API rather than a rejected\n // request. Named here so the fix is `.cloudignore` or object storage.\n skipped.push({\n path: relativePath,\n reason: `${Math.round(buffer.byteLength / 1024 / 1024)}MB, over the ${MAX_FILE_BYTES / 1024 / 1024}MB limit`,\n });\n continue;\n }\n const text = buffer.toString(\"utf-8\");\n if (Buffer.from(text, \"utf-8\").equals(buffer)) {\n files[relativePath] = text;\n } else {\n binaryFiles[relativePath] = buffer.toString(\"base64\");\n }\n }\n }\n }\n\n await walk(dir);\n return { files, binaryFiles, skipped };\n}\n\n/**\n * Format a deployment status with color.\n */\nexport function formatStatus(status: string): string {\n switch (status) {\n case \"running\":\n return chalk.green(status);\n case \"building\":\n case \"deploying\":\n return chalk.yellow(status);\n case \"failed\":\n case \"crashed\":\n return chalk.red(status);\n case \"stopped\":\n return chalk.gray(status);\n default:\n return status;\n }\n}\n\n/**\n * Format a table row for display.\n */\nexport function formatRow(columns: string[], widths: number[]): string {\n return columns.map((col, i) => col.padEnd(widths[i] || 20)).join(\" \");\n}\n\n/**\n * Require an API key or exit.\n */\nexport function requireApiKey(apiKey: string | undefined): asserts apiKey is string {\n if (!apiKey) {\n console.error(chalk.red(\"Not logged in. Run `videan login` first.\"));\n process.exit(1);\n }\n}\n\n/**\n * Require an organization or exit. Every app lives in an org, so commands that\n * list or create apps need one from --org or the saved default.\n */\nexport function requireOrg(orgId: string | undefined): string {\n if (!orgId) {\n console.error(chalk.red(\"No organization. Pass --org <id> or run `videan login` again.\"));\n process.exit(1);\n }\n return orgId;\n}\n\n// Re-exported rather than redefined: the CLI and the API have to agree on the\n// hostname, and they did not while each built it by hand.\nexport { appUrl } from \"@cloud/shared\";\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { DEFAULT_API_URL } from \"@cloud/shared\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\n/**\n * Downloads a snapshot back out as a folder.\n *\n * The dashboard can read any version file by file; this is the bulk direction \u2014\n * the \"I lost my local copy\" recovery, and the honest completion of \"the deploy\n * history IS the version history\": a history you cannot restore from is a\n * museum. Defaults to the newest snapshot; any snapshot id restores any\n * version.\n *\n * Refuses a non-empty directory without --force: pull overwrites what it\n * lands on, and a half-mixed tree (their edits + an old snapshot) is worse\n * than either side whole.\n */\nexport const pullCommand = new Command(\"pull\")\n .description(\"Download a snapshot's files into a directory\")\n .argument(\"[snapshot]\", \"Snapshot id (defaults to the newest)\")\n .option(\"-a, --app <id>\", \"App ID to pull from\")\n .option(\"-o, --out <dir>\", \"Directory to write into\", \".\")\n .option(\"--force\", \"Write into a non-empty directory\")\n .action(async (snapshotArg, opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n // Same default the SDK applies: an agent with only VIDEAN_API_KEY set has\n // no saved config and no reason to name the production URL. DEFAULT_API_URL\n // carries a /v1 suffix (the SDK's paths omit it; this file's include it),\n // so strip it to one convention.\n const baseUrl = (getBaseUrl() || DEFAULT_API_URL).replace(/\\/v1\\/?$/, \"\");\n\n const get = async <T>(p: string): Promise<T> => {\n const res = await fetch(`${baseUrl}${p}`, {\n headers: { authorization: `Bearer ${apiKey}` },\n });\n if (!res.ok) {\n const body = (await res.json().catch(() => null)) as { error?: { message?: string } } | null;\n throw new Error(body?.error?.message || `${res.status} from ${p}`);\n }\n return ((await res.json()) as { data: T }).data;\n };\n\n const spinner = ora(\"Resolving snapshot...\").start();\n try {\n let snapshotId: string = snapshotArg ?? \"\";\n if (!snapshotId) {\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n spinner.fail(chalk.red(\"No app specified. Use --app, VIDEAN_APP_ID, or name a snapshot id.\"));\n process.exit(1);\n }\n const [latest] = await get<{ id: string; seq?: number | null }[]>(\n `/v1/apps/${appId}/snapshots?limit=1`,\n );\n if (!latest) {\n spinner.fail(chalk.red(\"This app has no snapshots yet.\"));\n process.exit(1);\n }\n snapshotId = latest.id;\n }\n\n const detail = await get<{ manifest: string[]; seq?: number | null }>(\n `/v1/snapshots/${snapshotId}`,\n );\n\n const outDir = path.resolve(opts.out);\n await fs.ensureDir(outDir);\n const existing = await fs.readdir(outDir);\n if (existing.length > 0 && !opts.force) {\n spinner.fail(chalk.red(`${outDir} is not empty. Pass --force to write into it anyway.`));\n process.exit(1);\n }\n\n spinner.text = `Pulling ${detail.manifest.length} files...`;\n let written = 0;\n for (const file of detail.manifest) {\n const body = await get<{ content: string; encoding: string }>(\n `/v1/snapshots/${snapshotId}/files/${file}`,\n );\n const target = path.join(outDir, file);\n // The manifest is server-listed S3 keys, but never write outside the\n // destination on any input.\n if (!target.startsWith(outDir + path.sep) && target !== outDir) continue;\n await fs.ensureDir(path.dirname(target));\n await fs.writeFile(\n target,\n body.encoding === \"base64\" ? Buffer.from(body.content, \"base64\") : body.content,\n );\n written++;\n spinner.text = `Pulling ${written}/${detail.manifest.length}...`;\n }\n\n spinner.succeed(\n chalk.green(\n `Pulled snapshot ${detail.seq ? `#${detail.seq}` : snapshotId} \u2014 ${written} files into ${outDir}`,\n ),\n );\n } catch (err) {\n spinner.fail(chalk.red(err instanceof Error ? err.message : String(err)));\n process.exit(1);\n }\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\nexport const logsCommand = new Command(\"logs\")\n .description(\"View app logs\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-f, --follow\", \"Follow log output\")\n .option(\"-n, --lines <n>\", \"Number of lines to show\", \"50\")\n .option(\"-s, --since <when>\", \"Start of the window: ISO timestamp or an offset like 15m, 2h, 7d\", \"1h\")\n .option(\"--until <when>\", \"End of the window (same formats as --since)\")\n .option(\"-q, --search <text>\", \"Only lines containing this text\")\n .option(\"--pattern <pattern>\", \"CloudWatch filter pattern, for the real syntax\")\n .option(\"--instance <prefix>\", \"Only one instance, by log stream prefix\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n\n if (opts.search && opts.pattern) {\n console.error(chalk.red(\"Use either --search or --pattern, not both.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const filters = {\n q: opts.search as string | undefined,\n pattern: opts.pattern as string | undefined,\n instance: opts.instance as string | undefined,\n };\n\n if (opts.follow) {\n // The generator holds the cursor, so a line is printed once. The old loop\n // re-fetched the same window every two seconds and reprinted all of it.\n for await (const entry of client.apps.followLogs(appId, { since: opts.since, ...filters })) {\n print(entry);\n }\n return;\n }\n\n const page = await client.apps.logs(appId, {\n limit: parseInt(opts.lines, 10),\n since: opts.since,\n until: opts.until,\n ...filters,\n });\n\n // Oldest first, so the newest line is the one left next to the prompt.\n for (const entry of page.events) print(entry);\n\n if (page.events.length === 0) {\n console.log(chalk.gray(`No log lines since ${page.start}.`));\n }\n if (page.truncated) {\n // Said out loud: otherwise a window with more lines than one scan can read\n // looks like a window that simply contained these.\n console.log(\n chalk.yellow(\n \"\\nThis is part of the window -- there are more lines than one request reads. Narrow --since or add --search.\",\n ),\n );\n }\n });\n\nfunction print(entry: { timestamp: string; level?: string; message: string }): void {\n console.log(`${chalk.gray(entry.timestamp)} ${formatLevel(entry.level)} ${entry.message}`);\n}\n\nfunction formatLevel(level?: string): string {\n switch (level) {\n case \"error\":\n return chalk.red(\"[ERR]\");\n case \"warn\":\n return chalk.yellow(\"[WRN]\");\n case \"info\":\n return chalk.blue(\"[INF]\");\n case \"debug\":\n return chalk.gray(\"[DBG]\");\n default:\n return chalk.gray(\"[LOG]\");\n }\n}\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\n/**\n * `videan metrics` -- the window summary, and optionally the buckets.\n *\n * Summary by default rather than the series: a terminal cannot plot 60 buckets\n * usefully, and the question being asked (\"is anything hitting my app, and is it\n * erroring\") is answered by the totals. `--series` prints the buckets for piping\n * somewhere that can graph them.\n */\nexport const metricsCommand = new Command(\"metrics\")\n .description(\"Traffic, latency and resource use for an app\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-s, --since <when>\", \"Start of the window: ISO timestamp or an offset like 15m, 2h, 7d\", \"1h\")\n .option(\"--until <when>\", \"End of the window (same formats as --since)\")\n .option(\"--series\", \"Print each bucket instead of just the summary\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const metrics = await client.apps.metrics(appId, { since: opts.since, until: opts.until });\n\n if (opts.json) {\n console.log(JSON.stringify(metrics, null, 2));\n return;\n }\n\n const t = metrics.totals;\n console.log(\n chalk.dim(`${metrics.start} \u2192 ${metrics.end} (${metrics.period}s buckets)`),\n );\n console.log();\n console.log(` ${label(\"Requests\")} ${t.requests}`);\n console.log(` ${label(\"4xx\")} ${count(t.clientErrors)}`);\n console.log(` ${label(\"5xx\")} ${count(t.serverErrors, true)}`);\n // Named for what it is rather than \"auth\": these are visitors the gate turned\n // away, which is the answer when an owner says their app looks idle.\n console.log(` ${label(\"Gate denied\")} ${count(t.gateDenied)}`);\n console.log(` ${label(\"Latency avg\")} ${ms(t.latencyAvgMs)}`);\n console.log(` ${label(\"Latency p95\")} ${ms(t.latencyP95Ms)} ${chalk.dim(\"(sampled)\")}`);\n console.log(` ${label(\"CPU avg\")} ${units(t.cpuAvg)}`);\n console.log(` ${label(\"Memory avg\")} ${mb(t.memoryAvgMb)}`);\n\n if (t.requests === 0) {\n // A gap is not published as a zero, so an app that has never been reached and\n // an app that is not deployed look the same here.\n console.log();\n console.log(chalk.dim(\"No requests recorded in this window.\"));\n }\n\n if (opts.series) {\n console.log();\n for (const [name, points] of Object.entries(metrics.series)) {\n if (points.length === 0) continue;\n console.log(chalk.bold(name));\n for (const p of points) console.log(` ${chalk.dim(p.t)} ${p.v}`);\n }\n }\n });\n\nfunction label(text: string): string {\n return chalk.dim(text.padEnd(13));\n}\n\nfunction count(n: number, severe = false): string {\n if (n === 0) return chalk.dim(\"0\");\n return severe ? chalk.red(String(n)) : chalk.yellow(String(n));\n}\n\n/** Null means nothing was reported, which is not the same as zero. */\nfunction ms(v: number | null): string {\n return v === null ? chalk.dim(\"\u2014\") : `${v} ms`;\n}\n\nfunction mb(v: number | null): string {\n return v === null ? chalk.dim(\"\u2014\") : `${v} MB`;\n}\n\nfunction units(v: number | null): string {\n return v === null ? chalk.dim(\"\u2014\") : `${v} CPU units`;\n}\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\nexport const rollbackCommand = new Command(\"rollback\")\n .description(\"Rollback to the previous production deployment\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const spinner = ora(\"Rolling back...\").start();\n\n try {\n const deployment = await client.apps.rollback(appId);\n spinner.succeed(chalk.green(\"Rollback successful!\"));\n if (deployment.url) {\n console.log(chalk.cyan(` URL: ${deployment.url}`));\n }\n } catch (error: any) {\n spinner.fail(chalk.red(`Rollback failed: ${error.message}`));\n process.exit(1);\n }\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { prompt } from \"../prompt.js\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey, formatRow } from \"../util.js\";\n\nexport const secretsCommand = new Command(\"secrets\")\n .description(\"Manage app secrets\");\n\nsecretsCommand\n .command(\"set <key> [value]\")\n .description(\"Set a secret\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (key: string, value: string | undefined, opts: any) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified.\"));\n process.exit(1);\n }\n\n if (!value) {\n const answers = await prompt<{ value: string }>(\n [{ type: \"password\", name: \"value\", message: `Value for ${key}:` }],\n `Pass the value as an argument: videan secrets set ${key} <value>`,\n );\n value = answers.value;\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n await client.secrets.set(appId, key, value!);\n console.log(chalk.green(`Secret \"${key}\" set successfully.`));\n });\n\nsecretsCommand\n .command(\"list\")\n .description(\"List all secrets\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (opts: any) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const secrets = await client.secrets.list(appId);\n\n if (secrets.length === 0) {\n console.log(chalk.gray(\"No secrets configured.\"));\n return;\n }\n\n console.log(formatRow([\"KEY\", \"UPDATED\"], [30, 24]));\n for (const secret of secrets) {\n console.log(formatRow([secret.key, secret.updatedAt], [30, 24]));\n }\n });\n\nsecretsCommand\n .command(\"remove <key>\")\n .description(\"Remove a secret\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (key: string, opts: any) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n await client.secrets.delete(appId, key);\n console.log(chalk.green(`Secret \"${key}\" removed.`));\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { prompt } from \"../prompt.js\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp, getDefaultOrg, setCurrentApp } from \"../config.js\";\nimport { requireApiKey, requireOrg, formatRow, appUrl } from \"../util.js\";\nimport {\n DEFAULT_VISIBILITY,\n VISIBILITIES,\n VISIBILITY_LABELS,\n isVisibility,\n visibilityOf,\n APP_MODES,\n isAppMode,\n type App,\n type AppMode,\n type Visibility,\n} from \"@cloud/shared\";\n\nexport const appsCommand = new Command(\"apps\")\n .description(\"Manage apps\");\n\n/**\n * Validates `--access` here rather than letting the API answer 400.\n *\n * A tier is a security setting, so a typo has to fail loudly and name the legal\n * values \u2014 and it must fail *before* the rest of a `set` call is applied, since\n * a partially-applied settings write is worse than a refused one.\n */\nfunction accessOption(value: string | undefined): Visibility | undefined {\n if (value === undefined) return undefined;\n const tier = value.trim().toLowerCase();\n if (!isVisibility(tier)) {\n console.error(chalk.red(`Unknown --access value \"${value}\". Use one of: ${VISIBILITIES.join(\", \")}`));\n process.exit(1);\n }\n return tier;\n}\n\n/**\n * Validates `--mode` locally, for the same reason `--access` is validated here:\n * a mode that silently differs from the one asked for is the failure the\n * explicit field exists to prevent, and on `set` it must fail before any of the\n * other changes in the same call are applied.\n */\nfunction modeOption(value: string | undefined): AppMode | undefined {\n if (value === undefined) return undefined;\n const mode = value.trim().toLowerCase();\n if (!isAppMode(mode)) {\n console.error(chalk.red(`Unknown --mode value \"${value}\". Use one of: ${APP_MODES.join(\", \")}`));\n process.exit(1);\n }\n return mode;\n}\n\n/** Reads the tier from a response, falling back to the legacy boolean. */\nfunction describeAccess(app: Pick<App, \"authRequired\"> & { visibility?: string }): string {\n // An older API that does not send `visibility` still sends the boolean, and a\n // gated app is org-wide by default there.\n const tier = app.visibility ? visibilityOf(app.visibility) : app.authRequired ? \"org\" : \"public\";\n return `${tier} \u2014 ${VISIBILITY_LABELS[tier].toLowerCase()}`;\n}\n\nappsCommand\n .command(\"list\")\n .description(\"List all apps\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .action(async (opts: { org?: string }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const apps = await client.apps.list(orgId);\n\n if (apps.length === 0) {\n console.log(chalk.gray(\"No apps yet. Create one with `videan apps create`.\"));\n return;\n }\n\n const widths = [20, 24, 10, 44];\n console.log(formatRow([\"NAME\", \"ID\", \"STATUS\", \"URL\"], widths));\n for (const app of apps) {\n console.log(formatRow([app.name, app.id, app.status, appUrl(app.slug)], widths));\n }\n });\n\nappsCommand\n .command(\"create\")\n .description(\"Create a new app\")\n .option(\"-n, --name <name>\", \"App name\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .option(\"--postgres\", \"Provision a managed Postgres database\")\n // Both off unless asked for: storage outlives the code that wrote it and is\n // billed, and sending mail spends the platform's shared reputation.\n .option(\"--storage\", \"Give the app an object-storage prefix\")\n .option(\"--email\", \"Let the app send mail through the platform\")\n // The platform gates apps behind login by default; this opts out.\n .option(\"--public\", \"Serve the app without the platform login gate (same as --access public)\")\n .option(\"--access <tier>\", `Who may open the app: ${VISIBILITIES.join(\" | \")} (default: ${DEFAULT_VISIBILITY})`)\n .option(\n \"--stack <label>\",\n \"Group this app with its siblings (set the same label on a split frontend and backend)\",\n )\n .option(\n \"--mode <mode>\",\n `How the app runs: ${APP_MODES.join(\" | \")} (default: container). serverless scales to zero but runs no cron, jobs or WebSockets`,\n )\n .action(\n async (opts: {\n name?: string;\n org?: string;\n postgres?: boolean;\n storage?: boolean;\n email?: boolean;\n public?: boolean;\n access?: string;\n stack?: string;\n mode?: string;\n }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n let name = opts.name;\n if (!name) {\n const answers = await prompt<{ name: string }>(\n [{ type: \"input\", name: \"name\", message: \"App name:\" }],\n \"Pass the name: videan apps create --name <name>\",\n );\n name = answers.name;\n }\n if (!name) {\n console.error(chalk.red(\"An app name is required.\"));\n process.exit(1);\n }\n\n const visibility = accessOption(opts.access) ?? (opts.public ? \"public\" : undefined);\n const mode = modeOption(opts.mode);\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const app = await client.apps.create({\n name,\n orgId,\n postgres: opts.postgres ?? false,\n storage: opts.storage ?? false,\n email: opts.email ?? false,\n visibility,\n stack: opts.stack,\n mode,\n });\n setCurrentApp(app.id);\n\n console.log(chalk.green(`App \"${app.name}\" created (${app.id})`));\n console.log(chalk.gray(` Mode: ${app.mode ?? \"container\"}`));\n console.log(chalk.gray(` Access: ${describeAccess(app)}`));\n console.log(chalk.gray(` URL after first deploy: ${appUrl(app.slug)}`));\n // Runtime is detected from the pushed files, so there is nothing to pick here.\n console.log(chalk.gray(\" Push code with `videan deploy` \u2014 the runtime is auto-detected.\"));\n },\n );\n\nappsCommand\n .command(\"set\")\n .description(\"Change an app's settings (scale, rate limit, gate, capabilities)\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-n, --name <name>\", \"Rename the app (the URL keeps its original slug)\")\n .option(\"--cpu <units>\", \"CPU units per instance (256 = 0.25 vCPU)\")\n .option(\"--memory <mib>\", \"Memory per instance, in MiB\")\n .option(\"--instances <n>\", \"Number of instances to run\")\n .option(\"--rate-limit <rpm>\", \"Requests per minute per client\")\n .option(\"--no-rate-limit\", \"Remove the rate limit\")\n .option(\"--access <tier>\", `Who may open the app: ${VISIBILITIES.join(\" | \")}`)\n .option(\"--public\", \"Serve without the platform login gate (same as --access public)\")\n .option(\"--private\", \"Require platform login (leaves the tier alone if it is already gated)\")\n .option(\"--postgres\", \"Provision a managed Postgres database\")\n .option(\"--storage\", \"Give the app an object-storage prefix\")\n .option(\"--email\", \"Let the app send mail through the platform\")\n .option(\"--stack <label>\", \"Group this app with its siblings under a shared label\")\n .option(\"--no-stack\", \"Remove the app from its stack\")\n .option(\"--mode <mode>\", `How the app runs: ${APP_MODES.join(\" | \")} (applies on the next deploy or restart)`)\n .option(\"--json\", \"Print the raw response\")\n .action(\n async (opts: {\n app?: string;\n name?: string;\n cpu?: string;\n memory?: string;\n instances?: string;\n // commander sets this to false for --no-rate-limit and to the string for\n // --rate-limit, so the three states (untouched / removed / set) are\n // distinguishable: undefined, false, \"120\".\n rateLimit?: string | boolean;\n access?: string;\n public?: boolean;\n private?: boolean;\n postgres?: boolean;\n storage?: boolean;\n email?: boolean;\n // Same commander trick as rateLimit: --no-stack sets false, --stack sets\n // the string, untouched is undefined.\n stack?: string | boolean;\n mode?: string;\n json?: boolean;\n }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n\n if (opts.public && opts.private) {\n console.error(chalk.red(\"--public and --private contradict each other.\"));\n process.exit(1);\n }\n const access = accessOption(opts.access);\n if (access && (opts.public || opts.private)) {\n // Both would be sent and the API resolves `visibility` first, so the\n // boolean would be silently ignored. Refusing beats applying one of two\n // access settings the caller asked for.\n console.error(chalk.red(\"--access already says who may open the app; drop --public/--private.\"));\n process.exit(1);\n }\n\n const mode = modeOption(opts.mode);\n\n const changes: Parameters<CloudClient[\"apps\"][\"update\"]>[1] = {};\n if (opts.name) changes.name = opts.name;\n if (mode) changes.mode = mode;\n if (opts.cpu !== undefined) changes.cpu = intOption(\"--cpu\", opts.cpu);\n if (opts.memory !== undefined) changes.memory = intOption(\"--memory\", opts.memory);\n if (opts.instances !== undefined) {\n changes.instances = intOption(\"--instances\", opts.instances);\n }\n if (opts.rateLimit === false) changes.rateLimitPerMinute = null;\n else if (typeof opts.rateLimit === \"string\") {\n changes.rateLimitPerMinute = intOption(\"--rate-limit\", opts.rateLimit);\n }\n if (access) changes.visibility = access;\n if (opts.public) changes.authRequired = false;\n if (opts.private) changes.authRequired = true;\n if (opts.postgres) changes.postgres = true;\n if (opts.storage) changes.storage = true;\n if (opts.email) changes.email = true;\n if (opts.stack === false) changes.stack = null;\n else if (typeof opts.stack === \"string\") changes.stack = opts.stack;\n\n if (Object.keys(changes).length === 0) {\n // Otherwise this reports success having changed nothing, which reads as the\n // setting being applied.\n console.error(chalk.red(\"Nothing to change. Pass at least one option.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const app = await client.apps.update(appId, changes);\n\n if (opts.json) {\n console.log(JSON.stringify(app, null, 2));\n return;\n }\n\n console.log(chalk.green(`Updated ${app.name} (${app.id})`));\n console.log(` Mode: ${app.mode ?? \"container\"}`);\n console.log(` Access: ${describeAccess(app)}`);\n console.log(` Scale: ${app.cpu ?? \"default\"} CPU / ${app.memory ?? \"default\"} MiB`);\n console.log(\n ` Rate limit: ${app.rateLimitPerMinute ? `${app.rateLimitPerMinute}/min per client` : \"none\"}`,\n );\n if (app.restartRequired) {\n // Reported, not done: a restart is a brief interruption, and taking one as a\n // side effect of a settings write is not something to discover afterwards.\n console.log(\n chalk.yellow(\"\\n These settings apply to the next deploy. Run `videan deploy` to pick them up.\"),\n );\n }\n },\n );\n\n/** Parse a numeric flag, failing on junk rather than sending NaN to the API. */\nfunction intOption(flag: string, raw: string): number {\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n console.error(chalk.red(`${flag} expects a positive whole number, got \"${raw}\".`));\n process.exit(1);\n }\n return n;\n}\n\nappsCommand\n .command(\"delete <id>\")\n .description(\"Delete an app and all its resources\")\n .option(\"--yes\", \"Skip the confirmation prompt\")\n .action(async (id: string, opts: { yes?: boolean }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n if (!opts.yes) {\n const { confirm } = await prompt<{ confirm: boolean }>(\n [\n {\n type: \"confirm\",\n name: \"confirm\",\n message: `Delete app ${id}? This stops the app and destroys its database, secrets and snapshots.`,\n default: false,\n },\n ],\n \"Confirm with a flag: videan apps delete <id> --yes\",\n );\n if (!confirm) return;\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const { warnings } = await client.apps.delete(id);\n\n console.log(chalk.green(\"App deleted.\"));\n if (warnings.length > 0) {\n // Say so loudly: leaked AWS resources keep costing money.\n console.log(chalk.yellow(\"\\nSome resources could not be released:\"));\n for (const w of warnings) {\n console.log(chalk.yellow(` ${w.step}: ${w.error || \"unknown error\"}`));\n }\n }\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey, formatStatus, appUrl } from \"../util.js\";\nimport { VISIBILITY_LABELS, visibilityOf, type App } from \"@cloud/shared\";\n\n/**\n * Falls back to the legacy boolean, which is all an older API sends. A gated app\n * is org-wide there, so that is what the boolean means.\n */\nfunction accessLine(app: Pick<App, \"authRequired\"> & { visibility?: string }): string {\n const tier = app.visibility ? visibilityOf(app.visibility) : app.authRequired ? \"org\" : \"public\";\n return `${tier} \u2014 ${VISIBILITY_LABELS[tier].toLowerCase()}`;\n}\n\nexport const statusCommand = new Command(\"status\")\n .description(\"Show current app status\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const app = await client.apps.get(appId);\n // Health reads ECS rather than the deployment row, which keeps saying\n // \"running\" after the container dies.\n const health = await client.apps.health(appId);\n\n console.log(chalk.bold(app.name));\n console.log(` ID: ${app.id}`);\n console.log(` Created: ${app.createdAt}`);\n console.log(` Postgres: ${app.postgresEnabled ? \"enabled\" : \"disabled\"}`);\n console.log(` Access: ${accessLine(app)}`);\n console.log(` State: ${formatStatus(health.state)}`);\n console.log(` Healthy: ${health.healthy ? chalk.green(\"yes\") : chalk.red(\"no\")}`);\n console.log(` URL: ${chalk.cyan(health.url || appUrl(app.slug))}`);\n\n const task = health.task;\n if (task) {\n if (task.exit_code !== undefined && task.exit_code !== null) {\n const code = String(task.exit_code);\n console.log(` Exit code: ${task.exit_code === 0 ? code : chalk.red(code)}`);\n }\n if (task.stopped_reason) {\n console.log(` Stopped: ${chalk.yellow(task.stopped_reason)}`);\n }\n if (task.container_reason) {\n console.log(` Detail: ${chalk.yellow(task.container_reason)}`);\n }\n }\n\n if (!health.healthy) {\n console.log(chalk.gray(\"\\nRun `videan logs` to see why.\"));\n }\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { prompt } from \"../prompt.js\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getDefaultOrg } from \"../config.js\";\nimport { requireApiKey, requireOrg, formatRow } from \"../util.js\";\n\nexport const keysCommand = new Command(\"keys\")\n .description(\"Manage API keys for programmatic access\");\n\nkeysCommand\n .command(\"create <name>\")\n .description(\"Create an API key\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .option(\"-e, --expires <days>\", \"Expire the key after N days\")\n .option(\n \"--app <id>\",\n \"Pin the key to one app (a service token, for code running inside it)\",\n )\n .action(async (name: string, opts: { org?: string; expires?: string; app?: string }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n let expiresInDays: number | undefined;\n if (opts.expires !== undefined) {\n expiresInDays = Number(opts.expires);\n if (!Number.isInteger(expiresInDays) || expiresInDays < 1) {\n console.error(chalk.red(\"--expires must be a whole number of days, at least 1.\"));\n process.exit(1);\n }\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const key = await client.apiKeys.create({\n name,\n orgId,\n expiresInDays,\n appId: opts.app,\n });\n\n console.log(chalk.green(`Created key \"${key.name}\" (${key.id})`));\n // Printed once and never retrievable -- the server only stores a hash.\n console.log(`\\n ${chalk.bold(key.key)}\\n`);\n console.log(chalk.yellow(\"Copy it now. It cannot be shown again.\"));\n if (key.appId) {\n // Worth stating: this is the only kind of key that is safe to ship inside a\n // deployed app, and the difference is invisible in the key itself.\n console.log(\n chalk.gray(`Service token, scoped to app ${key.appId}. It cannot reach anything else.`),\n );\n } else {\n console.log(\n chalk.gray(\"Acts as you across every app in the org -- do not deploy it inside an app.\"),\n );\n }\n if (key.expiresAt) {\n console.log(chalk.gray(`Expires ${key.expiresAt}`));\n }\n });\n\nkeysCommand\n .command(\"list\")\n .description(\"List API keys\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .action(async (opts: { org?: string }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const keys = await client.apiKeys.list(orgId);\n\n if (keys.length === 0) {\n console.log(chalk.gray(\"No API keys. Create one with `videan keys create <name>`.\"));\n return;\n }\n\n const widths = [24, 24, 16, 22, 12, 24];\n console.log(formatRow([\"NAME\", \"ID\", \"PREFIX\", \"LAST USED\", \"STATE\", \"SCOPE\"], widths));\n for (const key of keys) {\n const state = key.revokedAt\n ? chalk.red(\"revoked\")\n : key.expiresAt && new Date(key.expiresAt) < new Date()\n ? chalk.yellow(\"expired\")\n : chalk.green(\"active\");\n console.log(formatRow(\n [\n key.name,\n key.id,\n `${key.keyPrefix}\u2026`,\n key.lastUsedAt || \"never\",\n state,\n key.appId ? `app ${key.appId}` : \"org-wide\",\n ],\n widths,\n ));\n }\n });\n\nkeysCommand\n .command(\"revoke <id>\")\n .description(\"Revoke an API key\")\n .option(\"--yes\", \"Skip the confirmation prompt\")\n .action(async (id: string, opts: { yes?: boolean }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n if (!opts.yes) {\n const { confirm } = await prompt<{ confirm: boolean }>(\n [{ type: \"confirm\", name: \"confirm\", message: `Revoke key ${id}?`, default: false }],\n \"Confirm with a flag: videan keys revoke <id> --yes\",\n );\n if (!confirm) return;\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n await client.apiKeys.revoke(id);\n console.log(chalk.green(\"Key revoked.\"));\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getDefaultOrg, setDefaultOrg } from \"../config.js\";\nimport { requireApiKey, requireOrg, formatRow } from \"../util.js\";\n\nexport const orgsCommand = new Command(\"orgs\")\n .description(\"Manage organizations\");\n\norgsCommand\n .command(\"list\")\n .description(\"List your organizations\")\n .action(async () => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const orgs = await client.orgs.list();\n const current = getDefaultOrg();\n\n if (orgs.length === 0) {\n console.log(chalk.gray(\"No organizations. Create one with `videan orgs create <name>`.\"));\n return;\n }\n\n const widths = [24, 24, 10, 8];\n console.log(formatRow([\"NAME\", \"ID\", \"PLAN\", \"\"], widths));\n for (const org of orgs) {\n console.log(formatRow(\n [org.name, org.id, org.plan, org.id === current ? chalk.green(\"default\") : \"\"],\n widths,\n ));\n }\n });\n\norgsCommand\n .command(\"create <name>\")\n .description(\"Create an organization\")\n .action(async (name: string) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const org = await client.orgs.create(name);\n // First org becomes the default so subsequent commands need no --org.\n if (!getDefaultOrg()) setDefaultOrg(org.id);\n\n console.log(chalk.green(`Organization \"${org.name}\" created (${org.id})`));\n });\n\norgsCommand\n .command(\"use <id>\")\n .description(\"Set the default organization for this machine\")\n .action(async (id: string) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n // Verify membership before saving, so a typo fails here instead of on every\n // later command.\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const org = await client.orgs.get(id);\n setDefaultOrg(org.id);\n\n console.log(chalk.green(`Default organization set to \"${org.name}\".`));\n });\n\norgsCommand\n .command(\"members\")\n .description(\"List organization members\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .action(async (opts: { org?: string }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const members = await client.orgs.members(orgId);\n\n const widths = [32, 24, 10];\n console.log(formatRow([\"EMAIL\", \"NAME\", \"ROLE\"], widths));\n for (const member of members) {\n console.log(formatRow(\n [member.email || member.user_id, member.name || \"-\", member.role],\n widths,\n ));\n }\n });\n\norgsCommand\n .command(\"invite <email>\")\n .description(\"Invite a member by email\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .action(async (email: string, opts: { org?: string }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n await client.orgs.invite(orgId, email);\n console.log(chalk.green(`Invited ${email}.`));\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey, formatRow } from \"../util.js\";\n\n/**\n * `videan storage` -- files an app keeps for itself, separate from its code.\n *\n * Upload and download go direct to S3 through a presigned URL, so a large file\n * does not travel through the control API. That is also why `put` reads and\n * writes real bytes rather than the text-only path `videan deploy` uses for\n * snapshots: this is where binaries belong.\n */\nexport const storageCommand = new Command(\"storage\").description(\"Store files for an app\");\n\nfunction client() {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n return new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n}\n\nfunction requireApp(opt?: string): string {\n const appId = opt || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n return appId;\n}\n\n/** 1.2 KB rather than 1234 -- the size is being read, not summed. */\nfunction human(bytes: number): string {\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n let n = bytes;\n let i = 0;\n while (n >= 1024 && i < units.length - 1) {\n n /= 1024;\n i++;\n }\n return `${i === 0 ? n : n.toFixed(1)} ${units[i]}`;\n}\n\nstorageCommand\n .command(\"ls [prefix]\")\n .description(\"List stored objects\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (prefix: string | undefined, opts: { app?: string; json?: boolean }) => {\n const appId = requireApp(opts.app);\n // Every page, not the first: a truncated listing that looks complete is how a\n // caller concludes a file was never uploaded.\n const objects = await client().storage.listAll(appId, prefix);\n\n if (opts.json) {\n console.log(JSON.stringify(objects, null, 2));\n return;\n }\n if (objects.length === 0) {\n console.log(chalk.gray(\"No objects stored. Upload one with `videan storage put <file>`.\"));\n return;\n }\n\n const widths = [52, 12, 24];\n console.log(formatRow([\"KEY\", \"SIZE\", \"MODIFIED\"], widths));\n for (const o of objects) {\n console.log(formatRow([o.key, human(o.size), o.lastModified || \"-\"], widths));\n }\n const total = objects.reduce((sum, o) => sum + o.size, 0);\n console.log(chalk.dim(`\\n${objects.length} object(s), ${human(total)}`));\n });\n\nstorageCommand\n .command(\"put <file> [key]\")\n .description(\"Upload a file (key defaults to the file's name)\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-t, --type <mime>\", \"Content type\")\n .action(async (file: string, key: string | undefined, opts: { app?: string; type?: string }) => {\n const appId = requireApp(opts.app);\n if (!(await fs.pathExists(file))) {\n console.error(chalk.red(`No such file: ${file}`));\n process.exit(1);\n }\n const body = await fs.readFile(file);\n const objectKey = key || path.basename(file);\n\n const stored = await client().storage.upload(appId, objectKey, body, {\n contentType: opts.type,\n });\n console.log(\n chalk.green(`Uploaded ${objectKey}`) + chalk.dim(` (${human(body.length)})`),\n );\n if (stored.etag) console.log(chalk.dim(`etag ${stored.etag}`));\n });\n\nstorageCommand\n .command(\"get <key> [file]\")\n .description(\"Download an object (defaults to the key's basename)\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (key: string, file: string | undefined, opts: { app?: string }) => {\n const appId = requireApp(opts.app);\n const bytes = await client().storage.download(appId, key);\n const dest = file || path.basename(key);\n await fs.writeFile(dest, bytes);\n console.log(chalk.green(`Wrote ${dest}`) + chalk.dim(` (${human(bytes.length)})`));\n });\n\nstorageCommand\n .command(\"rm <key>\")\n .description(\"Delete an object\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (key: string, opts: { app?: string }) => {\n const appId = requireApp(opts.app);\n await client().storage.delete(appId, key);\n // Deliberately not \"not found\" when it was already gone: delete is idempotent\n // server-side, and the state the caller wanted is the state they have.\n console.log(chalk.green(`Deleted ${key}`));\n });\n\nstorageCommand\n .command(\"url <key>\")\n .description(\"Print a temporary link to an object\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-e, --expires <seconds>\", \"Lifetime in seconds (30-3600)\", \"900\")\n .option(\"--upload\", \"Sign an upload (PUT) URL instead of a download\")\n .option(\"-d, --download <filename>\", \"Serve as a download with this filename\")\n .action(async (key: string, opts: any) => {\n const appId = requireApp(opts.app);\n const expiresIn = Number(opts.expires);\n if (!Number.isInteger(expiresIn) || expiresIn < 30 || expiresIn > 3600) {\n console.error(chalk.red(\"--expires must be a whole number of seconds between 30 and 3600.\"));\n process.exit(1);\n }\n\n const signed = await client().storage.presign(appId, key, {\n method: opts.upload ? \"put\" : \"get\",\n expiresIn,\n download: opts.download,\n });\n console.log(signed.url);\n // Said plainly because the URL looks like an ordinary link and is not one:\n // anyone who holds it has that access without logging in.\n console.error(\n chalk.yellow(\n `This ${signed.method} link works for ${expiresIn}s for anyone who has it.`,\n ),\n );\n });\n\nstorageCommand\n .command(\"usage\")\n .description(\"Total objects and bytes stored\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (opts: { app?: string }) => {\n const appId = requireApp(opts.app);\n const { objects, bytes } = await client().storage.usage(appId);\n console.log(`${objects} object(s), ${human(bytes)}`);\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\n/**\n * `videan email` -- send mail as an app, through the platform's verified domain.\n *\n * There is no `--from`. Mail goes out as `app-<slug>@<platform domain>`, which is\n * what keeps an app from sending as the platform's own `no-reply@` -- the address\n * password resets come from. `--reply-to` is the lever for routing replies\n * somewhere the app owns.\n */\nexport const emailCommand = new Command(\"email\").description(\"Send email from an app\");\n\nfunction client() {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n return new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n}\n\nfunction requireApp(opt?: string): string {\n const appId = opt || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n return appId;\n}\n\nemailCommand\n .command(\"send\")\n .description(\"Send an email\")\n .requiredOption(\"-t, --to <address...>\", \"Recipient address (repeatable)\")\n .requiredOption(\"-s, --subject <subject>\", \"Subject line\")\n .option(\"--text <body>\", \"Plain-text body\")\n .option(\"--html <body>\", \"HTML body\")\n .option(\"--body-file <path>\", \"Read the body from a file\")\n .option(\"--reply-to <address>\", \"Where replies should go\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"--json\", \"Print the raw response\")\n .action(\n async (opts: {\n to: string[];\n subject: string;\n text?: string;\n html?: string;\n bodyFile?: string;\n replyTo?: string;\n app?: string;\n json?: boolean;\n }) => {\n const appId = requireApp(opts.app);\n\n let text = opts.text;\n let html = opts.html;\n if (opts.bodyFile) {\n const contents = await fs.readFile(opts.bodyFile, \"utf-8\");\n // The extension decides which part it becomes: sending HTML as the plain\n // part delivers visible markup, which looks like a broken app rather than\n // a wrong flag.\n if (/\\.html?$/i.test(opts.bodyFile)) html = contents;\n else text = contents;\n }\n if (!text && !html) {\n console.error(chalk.red(\"Nothing to send. Pass --text, --html or --body-file.\"));\n process.exit(1);\n }\n\n const sent = await client().email.send(appId, {\n to: opts.to,\n subject: opts.subject,\n text,\n html,\n replyTo: opts.replyTo,\n });\n\n if (opts.json) {\n console.log(JSON.stringify(sent, null, 2));\n return;\n }\n\n console.log(chalk.green(\"Sent\"));\n console.log(` From: ${sent.from}`);\n console.log(` To: ${sent.to.join(\", \")}`);\n console.log(` Message: ${sent.messageId ?? \"(no id returned)\"}`);\n // Printed on every send: the quota is per day and shared with nothing else,\n // so the moment to notice it is running low is before it runs out.\n console.log(\n chalk.dim(\n ` Quota: ${sent.quota.sent}/${sent.quota.quota} today, resets ${sent.quota.resetsAt}`,\n ),\n );\n },\n );\n\nemailCommand\n .command(\"quota\")\n .description(\"Show today's sending allowance\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts: { app?: string; json?: boolean }) => {\n const quota = await client().email.quota(requireApp(opts.app));\n if (opts.json) {\n console.log(JSON.stringify(quota, null, 2));\n return;\n }\n console.log(`Sent today: ${quota.sent}`);\n console.log(`Daily quota: ${quota.quota}`);\n console.log(`Remaining: ${quota.remaining}`);\n console.log(chalk.dim(`Resets at: ${quota.resetsAt}`));\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\n/**\n * `videan jobs` -- the background queue.\n *\n * A job is a POST the platform makes to a path on the app itself, so there is no\n * worker to deploy: the handler is a route in the app, and `videan jobs enqueue`\n * is a way to trigger it out of band. Retries mean a handler can see the same job\n * twice -- `--key` is how a caller stops its *own* retry from becoming two jobs.\n */\nexport const jobsCommand = new Command(\"jobs\").description(\"Enqueue and inspect background jobs\");\n\nfunction client() {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n return new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n}\n\nfunction requireApp(opt?: string): string {\n const appId = opt || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or run `videan init` first.\"));\n process.exit(1);\n }\n return appId;\n}\n\n/** Parse a numeric flag, failing on junk rather than sending NaN to the API. */\nfunction intOption(flag: string, raw: string): number {\n const n = Number(raw);\n if (!Number.isInteger(n) || n < 0) {\n console.error(chalk.red(`${flag} expects a whole number, got \"${raw}\".`));\n process.exit(1);\n }\n return n;\n}\n\nconst STATUS_COLOR: Record<string, (s: string) => string> = {\n queued: chalk.yellow,\n running: chalk.cyan,\n succeeded: chalk.green,\n dead: chalk.red,\n cancelled: chalk.gray,\n};\n\nfunction statusLabel(status: string): string {\n return (STATUS_COLOR[status] ?? chalk.white)(status);\n}\n\njobsCommand\n .command(\"enqueue\")\n .description(\"Queue a job for the app to run\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-q, --queue <name>\", \"Queue name (default: default)\")\n .option(\"-p, --path <path>\", \"Path on the app to POST to (default: /jobs)\")\n .option(\"-d, --data <json>\", \"JSON payload\")\n .option(\"-f, --file <path>\", \"Read the JSON payload from a file\")\n .option(\"--delay <seconds>\", \"Run no earlier than this many seconds from now\")\n .option(\"--attempts <n>\", \"Attempts before the job is given up on (default: 3)\")\n .option(\"--timeout <seconds>\", \"How long the handler gets per attempt (default: 60)\")\n .option(\"--key <key>\", \"Idempotency key -- a repeat returns the same job\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts) => {\n const appId = requireApp(opts.app);\n\n // Read from a file or a flag, and refuse invalid JSON here rather than letting\n // the API reject it: the payload is usually the thing being iterated on, and a\n // round trip to say \"bad JSON\" is a slow way to learn about a typo.\n let payload: unknown;\n const raw = opts.file ? await fs.readFile(opts.file, \"utf-8\") : opts.data;\n if (raw !== undefined) {\n try {\n payload = JSON.parse(raw);\n } catch (e: any) {\n console.error(chalk.red(`--${opts.file ? \"file\" : \"data\"} is not valid JSON: ${e.message}`));\n process.exit(1);\n }\n }\n\n const job = await client().jobs.enqueue(appId, {\n queue: opts.queue,\n path: opts.path,\n payload,\n delaySeconds: opts.delay !== undefined ? intOption(\"--delay\", opts.delay) : undefined,\n maxAttempts: opts.attempts !== undefined ? intOption(\"--attempts\", opts.attempts) : undefined,\n timeoutSeconds: opts.timeout !== undefined ? intOption(\"--timeout\", opts.timeout) : undefined,\n idempotencyKey: opts.key,\n });\n\n if (opts.json) {\n console.log(JSON.stringify(job, null, 2));\n return;\n }\n\n if (job.deduplicated) {\n // Said plainly, because the alternative reading -- that the job was queued\n // again -- is the one that leads to work being counted twice.\n console.log(chalk.yellow(`Already queued as ${job.id} (idempotency key \"${opts.key}\")`));\n } else {\n console.log(chalk.green(`Queued ${job.id}`));\n }\n console.log(chalk.gray(` ${job.method} ${job.path} on queue \"${job.queue}\"`));\n if (job.runAt && Date.parse(job.runAt) > Date.now() + 1000) {\n console.log(chalk.gray(` Runs at ${new Date(job.runAt).toLocaleString()}`));\n }\n });\n\njobsCommand\n .command(\"list\")\n .description(\"List jobs, newest first\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-s, --status <status>\", \"queued, running, succeeded, dead or cancelled\")\n .option(\"-q, --queue <name>\", \"Only this queue\")\n .option(\"-n, --limit <n>\", \"How many to show (default: 20)\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts) => {\n const appId = requireApp(opts.app);\n const { jobs, counts } = await client().jobs.list(appId, {\n status: opts.status,\n queue: opts.queue,\n limit: opts.limit !== undefined ? intOption(\"--limit\", opts.limit) : 20,\n });\n\n if (opts.json) {\n console.log(JSON.stringify({ jobs, counts }, null, 2));\n return;\n }\n\n // Depth first: a page of 20 cannot say how far behind the queue is, and that\n // is the question someone running this is usually asking.\n const depth = Object.entries(counts)\n .map(([status, n]) => `${statusLabel(status)} ${n}`)\n .join(\" \");\n console.log(depth || chalk.gray(\"No jobs yet.\"));\n\n if (jobs.length === 0) return;\n console.log();\n for (const job of jobs) {\n const attempts = job.attempts > 0 ? chalk.gray(` ${job.attempts}/${job.maxAttempts}`) : \"\";\n console.log(\n `${statusLabel(job.status).padEnd(20)} ${chalk.white(job.id)} ${chalk.gray(\n `${job.queue} ${job.path}`,\n )}${attempts}`,\n );\n // Only the last error, and only when there is one: a queued job with an\n // error is a job that failed and is waiting to be retried, which reads as\n // healthy without it.\n if (job.error) console.log(chalk.red(` ${job.error}`));\n }\n });\n\njobsCommand\n .command(\"get <job-id>\")\n .description(\"Show one job in full\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (jobId, opts) => {\n const job = await client().jobs.get(requireApp(opts.app), jobId);\n if (opts.json) {\n console.log(JSON.stringify(job, null, 2));\n return;\n }\n console.log(`${chalk.white(job.id)} ${statusLabel(job.status)}`);\n console.log(chalk.gray(` ${job.method} ${job.path} on queue \"${job.queue}\"`));\n console.log(chalk.gray(` Attempts: ${job.attempts}/${job.maxAttempts}`));\n if (job.statusCode) console.log(chalk.gray(` Last response: HTTP ${job.statusCode}`));\n if (job.durationMs) console.log(chalk.gray(` Took: ${job.durationMs}ms`));\n if (job.error) console.log(chalk.red(` Error: ${job.error}`));\n if (job.status === \"queued\" && job.attempts > 0) {\n console.log(chalk.yellow(` Retrying at ${new Date(job.runAt).toLocaleString()}`));\n }\n if (job.payload !== null && job.payload !== undefined) {\n console.log(chalk.gray(\" Payload:\"));\n console.log(\n JSON.stringify(job.payload, null, 2)\n .split(\"\\n\")\n .map((l) => ` ${l}`)\n .join(\"\\n\"),\n );\n }\n });\n\njobsCommand\n .command(\"retry <job-id>\")\n .description(\"Run a dead job again, from a clean attempt count\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (jobId, opts) => {\n const job = await client().jobs.retry(requireApp(opts.app), jobId);\n console.log(chalk.green(`Requeued ${job.id}`));\n });\n\njobsCommand\n .command(\"cancel <job-id>\")\n .description(\"Cancel a job that has not started yet\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (jobId, opts) => {\n const job = await client().jobs.cancel(requireApp(opts.app), jobId);\n console.log(chalk.green(`Cancelled ${job.id}`));\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { CloudClient, type UsageQuantity } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp, getDefaultOrg } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\n/**\n * `videan usage` -- what the month costs, and where it went.\n *\n * Org-level by default, because that is the number that gets invoiced; `--app`\n * narrows to one app's share. The two do not add up to the same figure on purpose:\n * the plan's included allowance applies once to the org, so per-app lines are list\n * price with no allowance and the org total subtracts it exactly once.\n *\n * Nothing here changes a plan or pays anything. `videan usage plans` prints the\n * catalogue so an upgrade can be priced before anyone talks to a card.\n */\nexport const usageCommand = new Command(\"usage\")\n .description(\"Metered usage and cost for the current month\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .option(\"-a, --app <id>\", \"Show one app's share instead of the whole org\")\n .option(\"-p, --period <YYYY-MM>\", \"Month to report on (default: the month in progress)\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n\n if (opts.app || (!opts.org && getCurrentApp() && !getDefaultOrg())) {\n const appId = opts.app || getCurrentApp()!;\n const report = await client.apps.usage(appId, opts.period);\n if (opts.json) return void console.log(JSON.stringify(report, null, 2));\n\n console.log(chalk.dim(`${period(report.periodStart, report.periodEnd)} app ${appId}`));\n console.log();\n printMetrics(report.metrics);\n console.log();\n for (const line of report.lines) {\n console.log(` ${chalk.dim(line.label.padEnd(22))} ${money(line.cents)}`);\n }\n console.log(` ${chalk.bold(\"Share of the bill\".padEnd(22))} ${chalk.bold(report.cost)}`);\n console.log();\n console.log(\n chalk.dim(\"At list price, with no plan allowance applied \u2014 see `videan usage` for the org total.\"),\n );\n return;\n }\n\n const orgId = opts.org || getDefaultOrg();\n if (!orgId) {\n console.error(chalk.red(\"No org specified. Use --org, or --app for a single app.\"));\n process.exit(1);\n }\n\n const report = await client.orgs.usage(orgId, opts.period);\n if (opts.json) return void console.log(JSON.stringify(report, null, 2));\n\n const inv = report.invoice;\n console.log(\n chalk.dim(`${period(report.periodStart, report.periodEnd)} org ${orgId} plan ${report.plan}`),\n );\n console.log();\n printMetrics(report.metrics);\n\n if (report.apps.length > 0) {\n console.log();\n console.log(chalk.bold(\"By app\"));\n // Descending, because the question is which app is costing the money.\n for (const app of [...report.apps].sort((a, b) => b.cents - a.cents)) {\n const name = app.slug || app.name || app.appId || \"(deleted apps)\";\n console.log(` ${chalk.dim(String(name).padEnd(24))} ${money(app.cents)}`);\n }\n }\n\n console.log();\n for (const line of inv.lines) {\n console.log(\n ` ${chalk.dim(line.label.padEnd(22))} ${money(line.cents)} ${chalk.dim(\n `(${line.quantity} \u00D7 ${line.unitPriceCents}\u00A2)`,\n )}`,\n );\n }\n console.log(` ${chalk.dim(\"Usage\".padEnd(22))} ${money(inv.usageCents)}`);\n if (inv.includedUsageCents > 0) {\n console.log(\n ` ${chalk.dim(\"Included\".padEnd(22))} ${chalk.green(`-${money(Math.min(inv.usageCents, inv.includedUsageCents))}`)}`,\n );\n }\n if (inv.baseCents > 0) {\n console.log(` ${chalk.dim(\"Plan\".padEnd(22))} ${money(inv.baseCents)}`);\n }\n console.log(` ${chalk.bold(\"Total\".padEnd(22))} ${chalk.bold(report.total)}`);\n console.log();\n // Said explicitly: the month is not over, so this is what has accrued rather\n // than what will be charged.\n console.log(chalk.dim(\"Usage to date. The period is priced as it stands right now.\"));\n });\n\nusageCommand\n .command(\"plans\")\n .description(\"The plan catalogue and what each one allows\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts) => {\n // No key needed by the API, but the client requires one; an empty string is\n // enough and keeps `videan usage plans` working before login.\n const client = new CloudClient({ apiKey: getApiKey() || \"\", baseUrl: getBaseUrl() });\n const plans = await client.plans();\n if (opts.json) return void console.log(JSON.stringify(plans, null, 2));\n\n for (const plan of plans) {\n console.log(\n `${chalk.bold(plan.name)} ${chalk.dim(`(${plan.id})`)} ${plan.base}/mo` +\n (plan.includedUsageCents > 0 ? chalk.dim(`, ${plan.includedUsage} usage included`) : \"\"),\n );\n const l = plan.limits;\n console.log(\n chalk.dim(\n ` ${l.apps} apps \u00B7 ${l.instancesPerApp} instances/app \u00B7 up to ${l.maxCpu} CPU / ${l.maxMemory} MB \u00B7 ${l.maxStorageMb} MB storage` +\n `${l.postgres ? \" \u00B7 postgres\" : \"\"}${l.customDomains ? \" \u00B7 custom domains\" : \"\"}`,\n ),\n );\n console.log();\n }\n });\n\nfunction printMetrics(metrics: UsageQuantity[]): void {\n if (metrics.length === 0) {\n console.log(chalk.dim(\" Nothing metered in this period yet.\"));\n return;\n }\n for (const m of metrics) {\n console.log(` ${chalk.dim(m.metric.padEnd(22))} ${round(m.quantity)}`);\n }\n}\n\n/** Two decimals, and no trailing `.00` on a whole number. */\nfunction round(n: number): string {\n const v = Math.round(n * 100) / 100;\n return Number.isInteger(v) ? String(v) : v.toFixed(2);\n}\n\nfunction money(cents: number): string {\n return `$${(cents / 100).toFixed(2)}`;\n}\n\nfunction period(start: string, end: string): string {\n return `${start.slice(0, 10)} \u2192 ${end.slice(0, 10)}`;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { loginCommand } from \"./commands/login.js\";\nimport { logoutCommand } from \"./commands/logout.js\";\nimport { initCommand } from \"./commands/init.js\";\nimport { deployCommand } from \"./commands/deploy.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { logsCommand } from \"./commands/logs.js\";\nimport { metricsCommand } from \"./commands/metrics.js\";\nimport { rollbackCommand } from \"./commands/rollback.js\";\nimport { secretsCommand } from \"./commands/secrets.js\";\nimport { appsCommand } from \"./commands/apps.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { keysCommand } from \"./commands/keys.js\";\nimport { orgsCommand } from \"./commands/orgs.js\";\nimport { storageCommand } from \"./commands/storage.js\";\nimport { emailCommand } from \"./commands/email.js\";\nimport { jobsCommand } from \"./commands/jobs.js\";\nimport { usageCommand } from \"./commands/usage.js\";\nimport { installPromptCrashGuard } from \"./prompt.js\";\n\ninstallPromptCrashGuard();\n\nconst program = new Command();\n\nprogram\n .name(\"videan\")\n .description(\"Videan CLI \u2014 deploy apps in seconds\")\n // Keep in step with package.json on every release: this is what\n // `videan --version` prints, and a support report quoting a stale number\n // sends the investigation to the wrong code.\n .version(\"0.5.3\");\n\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(initCommand);\nprogram.addCommand(deployCommand);\nprogram.addCommand(pullCommand);\nprogram.addCommand(logsCommand);\nprogram.addCommand(metricsCommand);\nprogram.addCommand(rollbackCommand);\nprogram.addCommand(secretsCommand);\nprogram.addCommand(appsCommand);\nprogram.addCommand(statusCommand);\nprogram.addCommand(keysCommand);\nprogram.addCommand(orgsCommand);\nprogram.addCommand(storageCommand);\nprogram.addCommand(emailCommand);\nprogram.addCommand(jobsCommand);\nprogram.addCommand(usageCommand);\n\n/**\n * Commander does not await an async action's rejection, so anything a command\n * throws lands as an unhandled rejection and node prints a stack trace with\n * `triggerUncaughtException` at the top of it. That is what a user saw for a\n * connection reset. `parseAsync` gives the rejection somewhere to go; the\n * handler is the backstop for a throw outside a command's own promise.\n */\nfunction die(e: any): never {\n console.error(`Error: ${e?.message || e}`);\n // Only when asked: the message is the actionable part, and a stack trace on\n // every network blip trains people to ignore CLI output.\n if (process.env.VIDEAN_DEBUG && e?.stack) console.error(e.stack);\n process.exit(1);\n}\n\nprocess.on(\"unhandledRejection\", die);\n\nprogram.parseAsync().catch(die);\n", "import { Command } from \"commander\";\nimport inquirer from \"inquirer\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport os from \"node:os\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { DEFAULT_API_URL } from \"@cloud/shared\";\nimport { setApiKey, setDefaultOrg, getBaseUrl, setBaseUrl } from \"../config.js\";\nimport { prompt } from \"../prompt.js\";\nimport {\n consoleOriginFor,\n createCallbackServer,\n openBrowser,\n randomState,\n} from \"../browser-login.js\";\n\n/**\n * Finishes a password sign-in that answered `requires_2fa`.\n *\n * The password checked out but no token was issued: the account wants the\n * 6-digit code the API just emailed, redeemed against the opaque challenge\n * together via POST /auth/login/2fa. The server allows 5 attempts per\n * challenge and the code lives 10 minutes, so wrong codes re-prompt (with the\n * server's own message) rather than aborting -- a typo shouldn't cost a whole\n * new sign-in and a second email.\n *\n * Never reached on a non-TTY without a clean exit: the code arrives by email,\n * so there is nothing a flag or env var could supply -- non-interactive\n * callers should use VIDEAN_API_KEY instead of login (CLI convention: closed\n * stdin must refuse, not hang or crash).\n */\nexport async function completeTwoFactor(\n spinner: ReturnType<typeof ora>,\n baseUrl: string,\n login: { challenge?: string; code_email_sent?: boolean },\n): Promise<string> {\n if (!process.stdin.isTTY) {\n spinner.fail(\n \"This account has two-factor enabled: login needs a terminal to type the emailed code. \" +\n \"Non-interactively, set VIDEAN_API_KEY instead (create a key from a terminal or the dashboard).\",\n );\n process.exit(1);\n }\n\n spinner.stop();\n console.log(\n login.code_email_sent === false\n ? chalk.yellow(\"Two-factor is on, but the code email could not be sent. Trying anyway \u2014 check your inbox.\")\n : \"A 6-digit sign-in code was emailed to you (it expires in 10 minutes).\",\n );\n\n // The server spends an attempt per wrong code and caps at 5 per challenge;\n // stopping one short locally keeps the last server answer readable instead\n // of a generic \"too many wrong codes\" after the counter is already burned.\n for (let attempt = 0; attempt < 4; attempt++) {\n const { code } = await prompt<{ code: string }>(\n [\n {\n type: \"input\",\n name: \"code\",\n message: \"Sign-in code:\",\n validate: (v: string) => (/^\\d{6}$/.test(v.trim()) ? true : \"The code is 6 digits\"),\n },\n ],\n \"Two-factor login cannot be completed non-interactively; set VIDEAN_API_KEY instead.\",\n );\n\n let response: Response;\n try {\n response = await fetch(`${baseUrl}/auth/login/2fa`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ challenge: login.challenge, code: code.trim() }),\n });\n } catch (e: any) {\n spinner.fail(`Login failed: could not reach ${baseUrl} (${e?.message || e})`);\n process.exit(1);\n }\n\n if (response.ok) {\n const data = (await response.json()) as { data: { token: string } };\n return data.data.token;\n }\n\n const body = (await response.json().catch(() => null)) as\n | { error?: { message?: string } }\n | null;\n console.error(chalk.red(body?.error?.message || \"Invalid or expired sign-in code\"));\n }\n\n spinner.fail(\"Too many wrong codes. Run `videan login` again to get a new one.\");\n process.exit(1);\n}\n\n/**\n * Picks the org to scope the CLI to. Shared by every flow: an explicit --org is\n * verified rather than trusted (a key scoped to an org the user is not in fails\n * on every later call with no hint as to why), several orgs prompt on a TTY and\n * refuse off one, a single org is taken as-is.\n */\nasync function pickOrg(\n spinner: ReturnType<typeof ora>,\n session: CloudClient,\n orgOpt: string | undefined,\n resume: string,\n): Promise<string | undefined> {\n let orgs;\n try {\n orgs = await session.orgs.list();\n } catch (e: any) {\n spinner.fail(`Logged in, but could not list organizations: ${e?.message}`);\n process.exit(1);\n }\n\n if (orgOpt) {\n const match = orgs.find((o) => o.id === orgOpt || o.name === orgOpt);\n if (!match) {\n spinner.fail(`No organization ${orgOpt} for this account.`);\n process.exit(1);\n }\n return match.id;\n }\n if (orgs.length > 1 && !process.stdin.isTTY) {\n spinner.fail(`Several organizations (${orgs.map((o) => o.id).join(\", \")}). Pass --org <id>.`);\n process.exit(1);\n }\n if (orgs.length > 1) {\n spinner.stop();\n const pick = await inquirer.prompt([\n {\n type: \"list\",\n name: \"orgId\",\n message: \"Organization:\",\n choices: orgs.map((o) => ({ name: `${o.name} (${o.id})`, value: o.id })),\n },\n ]);\n spinner.start(resume);\n return pick.orgId;\n }\n // One org, or none yet -- the caller handles the empty case.\n return orgs[0]?.id;\n}\n\n/**\n * The common ending: trade a session token for an API key and save it.\n *\n * The login JWT expires in days. Trading it for a key keeps the CLI working,\n * and a stolen config file can then be revoked without a password change.\n * Requires an org, since keys are scoped to one.\n */\nasync function finishWithToken(\n spinner: ReturnType<typeof ora>,\n baseUrl: string,\n token: string,\n orgOpt: string | undefined,\n): Promise<void> {\n const session = new CloudClient({ apiKey: token, baseUrl });\n const orgId = await pickOrg(spinner, session, orgOpt, \"Creating CLI key...\");\n\n if (!orgId) {\n // Nothing to scope a key to yet; the session token still works for the\n // one call needed to create an org.\n setApiKey(token);\n spinner.warn(\n chalk.yellow(\"Logged in, but you have no organization yet. Run `videan orgs create <name>`.\"),\n );\n return;\n }\n\n try {\n const key = await session.apiKeys.create({\n name: `cli@${os.hostname()}`,\n orgId,\n });\n setApiKey(key.key);\n setDefaultOrg(orgId);\n spinner.succeed(chalk.green(\"Logged in.\"));\n console.log(chalk.gray(` Org: ${orgId}`));\n console.log(chalk.gray(` Key: ${key.keyPrefix}\u2026 (revoke with \\`videan keys revoke ${key.id}\\`)`));\n } catch (e: any) {\n // Fall back to the session token rather than leaving the user logged out.\n setApiKey(token);\n setDefaultOrg(orgId);\n spinner.warn(\n chalk.yellow(`Logged in with a temporary session (key creation failed: ${e?.message}).`),\n );\n }\n}\n\n/**\n * Browser sign-in: listen on a loopback port, send the user to the console's\n * /cli-auth page, and wait for it to deliver a session token. This is the only\n * login the CLI can offer an account with no password \u2014 Google- and SSO-only\n * accounts \u2014 and it is the default on a terminal because of that.\n */\nasync function browserLogin(consoleOrigin: string): Promise<string> {\n const state = randomState();\n const server = await createCallbackServer(state, undefined, consoleOrigin);\n const url = `${consoleOrigin}/cli-auth?port=${server.port}&state=${state}`;\n\n console.log(\"Opening your browser to sign in (password, Google or SSO all work):\");\n console.log(chalk.cyan(` ${url}`));\n console.log(chalk.gray(\" Waiting for the browser\u2026 (Ctrl-C to cancel)\"));\n openBrowser(url);\n\n return server.waitForToken;\n}\n\nasync function passwordLogin(\n spinner: ReturnType<typeof ora>,\n baseUrl: string,\n email: string,\n password: string,\n): Promise<string> {\n let login: {\n token?: string;\n requires_2fa?: boolean;\n challenge?: string;\n code_email_sent?: boolean;\n };\n try {\n const response = await fetch(`${baseUrl}/auth/login`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, password }),\n });\n\n if (!response.ok) {\n // Only 401 means the credentials are wrong. Everything else is the CLI\n // talking to the wrong place, and blaming the password for it sends the\n // user off resetting a password that was never the problem -- a stale\n // saved `baseUrl` pointing at a host that no longer serves /v1 reported\n // \"check your credentials\" for a plain 404.\n if (response.status === 401) {\n spinner.fail(\"Login failed. Check your credentials.\");\n } else {\n spinner.fail(`Login failed: ${response.status} from ${baseUrl}/auth/login`);\n console.error(\n chalk.gray(\" Wrong API URL? Re-run with --api-url <url>, or `videan logout` to reset.\"),\n );\n }\n process.exit(1);\n }\n\n const data = (await response.json()) as {\n data: {\n token?: string;\n requires_2fa?: boolean;\n challenge?: string;\n code_email_sent?: boolean;\n };\n };\n login = data.data;\n } catch (e: any) {\n spinner.fail(`Login failed: could not reach ${baseUrl} (${e?.message || e})`);\n process.exit(1);\n }\n\n if (login.requires_2fa) {\n return completeTwoFactor(spinner, baseUrl, login);\n }\n return login.token!;\n}\n\nexport const loginCommand = new Command(\"login\")\n .description(\"Log in to Cloud (opens your browser; --email/--password for the prompt flow)\")\n .option(\"-e, --email <email>\", \"Email \u2014 switches to the email/password flow\")\n .option(\"-p, --password <password>\", \"Password (skips the prompt; prefer VIDEAN_PASSWORD)\")\n .option(\"-o, --org <id>\", \"Organization to scope the key to (skips the picker)\")\n .option(\"-k, --key <api-key>\", \"Use an existing API key instead of signing in\")\n .option(\"--no-browser\", \"Never open a browser; prompt for email and password\")\n .option(\"--api-url <url>\", \"API base URL to use and remember (default: the public API)\")\n .action(async (opts) => {\n // Same base as the SDK uses, so a custom baseUrl works for both. Saved\n // before the request, so that a wrong --api-url can be corrected by another\n // login rather than by editing the config file by hand.\n if (opts.apiUrl) setBaseUrl(opts.apiUrl.replace(/\\/$/, \"\"));\n const baseUrl = (getBaseUrl() || DEFAULT_API_URL).replace(/\\/$/, \"\");\n\n // --key: no sign-in at all. Verify the key actually works before saving it,\n // otherwise every later command fails with no hint that login was the problem.\n if (opts.key) {\n const spinner = ora(\"Checking the key...\").start();\n const session = new CloudClient({ apiKey: opts.key, baseUrl });\n const orgId = await pickOrg(spinner, session, opts.org, \"Saving...\");\n setApiKey(opts.key);\n if (orgId) setDefaultOrg(orgId);\n spinner.succeed(chalk.green(\"Logged in with the provided key.\"));\n if (orgId) console.log(chalk.gray(` Org: ${orgId}`));\n return;\n }\n\n const password = opts.password || process.env.VIDEAN_PASSWORD || process.env.CLOUD_PASSWORD;\n const wantsPasswordFlow = Boolean(opts.email || password || opts.browser === false);\n const consoleOrigin = consoleOriginFor(baseUrl);\n\n // Browser flow is the default on a terminal: it is the only login that\n // works for Google- and SSO-only accounts, and the password flow is one\n // flag away. A base URL whose console we cannot derive (localhost, a bare\n // IP) falls back to the prompt flow rather than opening a wrong page.\n if (!wantsPasswordFlow && process.stdin.isTTY && consoleOrigin) {\n let token: string;\n try {\n token = await browserLogin(consoleOrigin);\n } catch (e: any) {\n console.error(chalk.red(`Browser sign-in failed: ${e?.message || e}`));\n console.error(chalk.gray(\" For the prompt flow instead: videan login --no-browser\"));\n process.exit(1);\n }\n const spinner = ora(\"Finishing up...\").start();\n await finishWithToken(spinner, baseUrl, token, opts.org);\n return;\n }\n\n // Anything not supplied has to be prompted for, and prompting needs a\n // terminal. Without this the command hung on a closed stdin and then died\n // inside readline with `ERR_USE_AFTER_CLOSE` -- an agent driving the CLI, the\n // platform's whole intended caller, could not log in and could not tell why.\n if ((!opts.email || !password) && !process.stdin.isTTY) {\n console.error(\n chalk.red(\n \"login needs a terminal to prompt. Non-interactively, pass --email and --password \" +\n \"(or VIDEAN_PASSWORD), pass --key <api-key>, or skip login entirely by setting VIDEAN_API_KEY.\",\n ),\n );\n process.exit(1);\n }\n\n const prompts = [\n ...(opts.email ? [] : [{ type: \"input\", name: \"email\", message: \"Email:\" }]),\n ...(password ? [] : [{ type: \"password\", name: \"password\", message: \"Password:\" }]),\n ];\n const answers = {\n email: opts.email,\n password,\n ...(prompts.length ? await inquirer.prompt(prompts) : {}),\n };\n\n const spinner = ora(\"Logging in...\").start();\n const token = await passwordLogin(spinner, baseUrl, answers.email, answers.password);\n await finishWithToken(spinner, baseUrl, token, opts.org);\n });\n", "export const DEFAULT_PORT = 3000;\n\nexport const SUPPORTED_RUNTIMES = [\n \"node-20\",\n \"node-22\",\n \"python-3.11\",\n \"python-3.12\",\n \"bun-1\",\n \"deno-2\",\n] as const;\n\nexport type SupportedRuntime = (typeof SUPPORTED_RUNTIMES)[number];\n\nexport const DEPLOYMENT_STATUSES = [\n \"building\",\n \"deploying\",\n \"running\",\n \"failed\",\n // A task that was live and then died on its own -- OOM, panic, ECS\n // replacement. The reconcile pass sets it, so anything enumerating statuses\n // has to know about it.\n \"crashed\",\n \"stopped\",\n] as const;\n\nexport const ENVIRONMENTS = [\"preview\", \"production\"] as const;\n\nexport const ROLES = [\"admin\", \"member\"] as const;\n\nexport const DEFAULT_API_URL = \"https://api.videan.xyz/v1\";\n\n/** The zone every app's hostname hangs off. */\nexport const APP_DOMAIN = process.env.CLOUD_APP_DOMAIN || \"videan.xyz\";\n\n/**\n * The public URL of an app, from its slug.\n *\n * One definition, because it was previously written out by hand in three places\n * and one of them omitted the scheme -- so a deployment's `url` came back as a\n * bare `slug.videan.xyz` that no terminal, browser or `fetch` would follow,\n * while the app resource reported the same app as `https://slug.videan.xyz`.\n * HTTP redirects to HTTPS anyway, so the scheme is not optional in practice.\n */\nexport function appUrl(slug: string): string {\n return `https://${slug}.${APP_DOMAIN}`;\n}\n\n/**\n * Prefix for a single deployment's own hostname.\n *\n * Every deployment is reachable before it is promoted, which is what lets an\n * agent verify its own work without production riding on the answer. The label is\n * a hash of the deployment id rather than the id itself: nanoid's alphabet\n * contains `_` and uppercase, neither of which is legal in a DNS label, and\n * lowercasing or substituting would map two distinct deployments onto one\n * hostname -- serving whichever the router looked up first.\n *\n * The prefix keeps the namespace from colliding with app slugs: `*.videan.xyz`\n * is one flat namespace, and an app legitimately slugged the same as some\n * deployment's hash would otherwise steal its traffic.\n */\nexport const PREVIEW_LABEL_PREFIX = \"d-\";\n\nexport function previewUrl(label: string): string {\n return `https://${PREVIEW_LABEL_PREFIX}${label}.${APP_DOMAIN}`;\n}\n\n/** True for a hostname label that belongs to the preview namespace. */\nexport function isPreviewLabel(label: string): boolean {\n return label.startsWith(PREVIEW_LABEL_PREFIX);\n}\n\nexport const CONFIG_FILE_NAME = \"cloud.yaml\";\n\nexport const IGNORE_FILE_NAME = \".cloudignore\";\n\nexport const MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; // 50MB\n\nexport const MAX_FILES_PER_PUSH = 10_000;\n", "/**\n * Runtime detection and Dockerfile generation.\n *\n * This is the single source of truth for \"what kind of app is this and how do we\n * containerize it\". The control API generates build contexts from it and the\n * builder service reports runtime from it; two implementations would eventually\n * disagree about the same snapshot.\n */\n\nexport type Runtime = \"custom\" | \"nextjs\" | \"nodejs\" | \"python\" | \"go\";\n\n/**\n * Every app is served on this port.\n *\n * The edge router resolves a task to `ip:APP_PORT`, so detection cannot pick a\n * per-runtime port -- a Python app on 8000 would simply be unroutable. Generated\n * Dockerfiles set PORT=3000 and EXPOSE 3000; a project supplying its own\n * Dockerfile must listen here too.\n */\nexport const APP_PORT = 3000;\n\nexport const RUNTIMES: Runtime[] = [\"custom\", \"nextjs\", \"nodejs\", \"python\", \"go\"];\n\n/**\n * Narrows a string that crossed a boundary as `string` -- the builder reports the\n * detected runtime as plain text, and a caller keying a table off it needs to know\n * the value is one of ours rather than assume it.\n */\nexport function isRuntime(value: string): value is Runtime {\n return (RUNTIMES as string[]).includes(value);\n}\n\nexport interface DetectionResult {\n runtime: Runtime;\n port: number;\n /** True when the project shipped its own Dockerfile and we generate nothing. */\n hasDockerfile: boolean;\n /** Why this runtime was chosen -- surfaced in build logs. */\n reason: string;\n}\n\nconst NEXT_CONFIG = /^next\\.config\\.(js|cjs|mjs|ts|mts)$/;\n\n/**\n * The AWS Lambda Web Adapter, injected into every generated image.\n *\n * It is what lets one contract (\"serve HTTP on port 3000\") run on Lambda at all:\n * Lambda speaks an invoke API, not HTTP, and the adapter translates between them\n * in front of an ordinary web server. The app itself needs no Lambda awareness,\n * which is the whole point -- the same code and the same image serve both\n * execution modes.\n *\n * Injected **unconditionally**, including for apps that will only ever run on\n * Fargate. `/opt/extensions` binaries are started by the Lambda runtime and by\n * nothing else, so under Fargate this is an unused ~4 MB file: inert, not\n * conditional. That is deliberate and load-bearing -- it means switching an app\n * between `container` and `serverless` is a pointer move over the image that\n * already exists, not a rebuild. A mode-conditional Dockerfile would make the\n * image mode-specific and turn every mode change into a full build.\n *\n * Pinned to an exact adapter version for the same reason every base image here is:\n * `:latest` would silently change what a rebuild of an unchanged snapshot produces.\n *\n * `AWS_LWA_READINESS_CHECK_PATH` is deliberately unset -- the adapter's default\n * is a TCP connect to the port, which is exactly the platform's contract. A path\n * would require every app to serve some agreed health route, which is a second\n * contract.\n */\nconst LAMBDA_ADAPTER = `COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter\nENV AWS_LWA_PORT=${APP_PORT}`;\n\n/**\n * Amazon's RDS CA bundle, installed into every generated Node image.\n *\n * The injected `DATABASE_URL` carries `sslmode=require`, which under libpq means\n * \"encrypt, do not verify\" -- so psycopg and pgx connect happily. `pg` >= 8.16\n * deliberately diverges and treats `require` as an alias for `verify-full`, and\n * the RDS roots are in no distribution's trust store, so **every** Node app that\n * opened its managed database died on `unable to get local issuer certificate`\n * after a successful build and a successful promotion. Reported as a crash, on\n * the one code path a tenant cannot work around from their own repo.\n *\n * Fixed by supplying the CA rather than by weakening the mode: `sslmode=no-verify`\n * would work for `pg` and is not a libpq mode at all, so it would break the two\n * runtimes that currently work, and one URL is injected for all of them. With the\n * bundle present, verify-full genuinely verifies.\n *\n * `ADD` fails the build if the fetch fails, which is the right direction: a\n * silently absent CA is the bug being fixed here.\n *\n * The `chmod` is what makes it work under Lambda as well as Fargate. Docker gives\n * a file fetched by `ADD <url>` mode 600 owned by root, and the generated node\n * images declare no `USER`, so under Fargate the app runs as root and reads it\n * fine -- but Lambda executes a container image as its own unprivileged sandbox\n * user, which cannot open a root-only file. Node's response to an unreadable\n * `NODE_EXTRA_CA_CERTS` is to warn and carry on with the default store, so the\n * symptom is not a startup failure but the original bug returning verbatim:\n * `unable to get local issuer certificate`, from an image whose environment\n * plainly names the bundle. Written as a `RUN` rather than `ADD --chmod=644`\n * because that flag needs BuildKit and CodeBuild builds these with the classic\n * builder.\n */\nconst RDS_CA = `# Lets a managed-Postgres connection be verified, not merely encrypted. See\n# packages/shared/src/detect.ts -- without this, \\`pg\\` cannot connect at all.\nADD https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem /etc/ssl/certs/rds-ca.pem\nRUN chmod 644 /etc/ssl/certs/rds-ca.pem\nENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/rds-ca.pem`;\n\n/**\n * Detects the runtime from a snapshot's file list.\n *\n * Paths are relative and may carry a leading \"./\". Order matters: a project can\n * satisfy several checks at once (a Next.js app has a package.json too), so the\n * most specific signal has to win.\n */\nexport function detectRuntime(files: string[]): DetectionResult {\n const set = new Set(files.map((f) => f.replace(/^\\.\\//, \"\")));\n\n // A hand-written Dockerfile always wins -- it is an explicit instruction.\n if (set.has(\"Dockerfile\") || set.has(\"dockerfile\")) {\n return {\n runtime: \"custom\",\n port: APP_PORT,\n hasDockerfile: true,\n reason: \"project provided a Dockerfile\",\n };\n }\n\n const hasPackageJson = set.has(\"package.json\");\n const hasNextConfig = [...set].some((f) => NEXT_CONFIG.test(f));\n const hasRequirements = set.has(\"requirements.txt\");\n const hasPyproject = set.has(\"pyproject.toml\");\n const hasGoMod = set.has(\"go.mod\");\n\n if (hasPackageJson && hasNextConfig) {\n return {\n runtime: \"nextjs\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found package.json and a next.config file\",\n };\n }\n\n if (hasPackageJson) {\n return {\n runtime: \"nodejs\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found package.json\",\n };\n }\n\n if (hasRequirements || hasPyproject) {\n return {\n runtime: \"python\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: hasRequirements ? \"found requirements.txt\" : \"found pyproject.toml\",\n };\n }\n\n if (hasGoMod) {\n return {\n runtime: \"go\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found go.mod\",\n };\n }\n\n throw new Error(\n \"Could not detect a runtime. Include a package.json, requirements.txt, \" +\n \"pyproject.toml, go.mod, or your own Dockerfile.\",\n );\n}\n\n/** The Python entry point to run, in order of preference. */\nfunction pythonEntry(files: string[]): string | null {\n const set = new Set(files.map((f) => f.replace(/^\\.\\//, \"\")));\n for (const candidate of [\"main.py\", \"app.py\", \"server.py\", \"manage.py\"]) {\n if (set.has(candidate)) return candidate;\n }\n return null;\n}\n\n/**\n * Generates the Dockerfile for a detected runtime.\n *\n * Throws for `custom`: that project has its own Dockerfile and overwriting it\n * would silently discard the author's build.\n */\nexport function generateDockerfile(files: string[], detection?: DetectionResult): string {\n const detected = detection ?? detectRuntime(files);\n\n if (detected.runtime === \"custom\") {\n throw new Error(\"Project supplies its own Dockerfile; nothing to generate\");\n }\n\n switch (detected.runtime) {\n case \"nextjs\":\n return `FROM public.ecr.aws/docker/library/node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm install\nCOPY . .\nRUN npm run build\n${RDS_CA}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV HOSTNAME=0.0.0.0\nEXPOSE ${APP_PORT}\nCMD [\"npm\", \"start\"]\n`;\n\n case \"nodejs\":\n return `FROM public.ecr.aws/docker/library/node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\n# npm ci needs a lockfile; fall back to install when there is none.\nRUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi\nCOPY . .\n${RDS_CA}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV HOST=0.0.0.0\nEXPOSE ${APP_PORT}\nCMD [\"npm\", \"start\"]\n`;\n\n case \"python\": {\n const entry = pythonEntry(files);\n // No recognisable entry point: install deps and let the image fail loudly\n // at start rather than guessing a module name.\n const cmd = entry\n ? `CMD [\"python\", \"${entry}\"]`\n : `CMD [\"sh\", \"-c\", \"echo 'No entry point found (expected main.py, app.py, server.py or manage.py)' >&2; exit 1\"]`;\n const install = `RUN if [ -f requirements.txt ]; then pip install --no-cache-dir -r requirements.txt; else pip install --no-cache-dir .; fi`;\n return `FROM public.ecr.aws/docker/library/python:3.12-slim\nWORKDIR /app\nCOPY . .\n${install}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV PYTHONUNBUFFERED=1\nEXPOSE ${APP_PORT}\n${cmd}\n`;\n }\n\n case \"go\":\n return `FROM public.ecr.aws/docker/library/golang:1.22-alpine AS builder\nWORKDIR /build\nCOPY go.* ./\nRUN go mod download\nCOPY . .\nRUN CGO_ENABLED=0 go build -o app .\n\nFROM public.ecr.aws/docker/library/alpine:3.20\nWORKDIR /app\nCOPY --from=builder /build/app .\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nEXPOSE ${APP_PORT}\nCMD [\"./app\"]\n`;\n }\n}\n\n/**\n * Derives an app's URL slug from its name.\n *\n * The slug becomes a DNS label (`<slug>.videan.xyz`), so it must be lowercase\n * alphanumeric with inner hyphens and at most 63 characters.\n */\nexport function slugify(name: string): string {\n return name\n .toLowerCase()\n .normalize(\"NFKD\")\n // Strip combining marks so \"Caf\u00E9\" becomes \"cafe\", not \"caf\".\n .replace(/[\u0300-\u036F]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 63)\n // Slicing can leave a trailing hyphen, which is not a valid DNS label.\n .replace(/-+$/g, \"\");\n}\n", "/**\n * Fargate task sizing.\n *\n * Every app used to run at 256 CPU / 512 MB, hardcoded in `ecs-app.ts`. That is\n * below the working set of a stock Next.js server, so those apps were killed by\n * the OOM killer -- which surfaces as a container that exits without a message,\n * indistinguishable from a crash in the app's own code. Sizing is therefore not\n * a tuning knob; at the old default some runtimes could not work at all.\n *\n * Fargate accepts only specific (cpu, memory) pairs and rejects everything else\n * at RegisterTaskDefinition time. Validating here rather than there means the\n * caller is told which values are legal instead of reading an AWS error, and the\n * CLI can reject a bad `--memory` before a build starts.\n */\n\nimport type { Runtime } from \"./detect.js\";\n\n/**\n * Legal memory values (MiB) for each CPU unit, from the Fargate task-size table.\n *\n * Expressed as a range plus a step rather than a literal list because the larger\n * tiers have hundreds of legal values; a list would be unreadable and would drift.\n */\nconst FARGATE_SIZES: Record<number, { min: number; max: number; step: number }> = {\n 256: { min: 512, max: 2048, step: 512 },\n 512: { min: 1024, max: 4096, step: 1024 },\n 1024: { min: 2048, max: 8192, step: 1024 },\n 2048: { min: 4096, max: 16384, step: 1024 },\n 4096: { min: 8192, max: 30720, step: 1024 },\n 8192: { min: 16384, max: 61440, step: 4096 },\n 16384: { min: 32768, max: 122880, step: 8192 },\n};\n\nexport const FARGATE_CPUS = Object.keys(FARGATE_SIZES)\n .map(Number)\n .sort((a, b) => a - b);\n\n/**\n * 256 CPU is a special case: its legal memory values are 512, 1024 and 2048 --\n * it skips 1536, so the uniform step above would admit a value Fargate rejects.\n */\nconst CPU_256_MEMORY = [512, 1024, 2048];\n\nexport interface SizeValidation {\n ok: boolean;\n /** Why the pair is illegal, naming the legal alternatives. */\n error?: string;\n /** The legal memory values for the given CPU, for building an error message. */\n allowed?: number[];\n}\n\nfunction legalMemory(cpu: number): number[] {\n if (cpu === 256) return CPU_256_MEMORY;\n const spec = FARGATE_SIZES[cpu];\n if (!spec) return [];\n const values: number[] = [];\n for (let m = spec.min; m <= spec.max; m += spec.step) values.push(m);\n return values;\n}\n\n/**\n * Checks a (cpu, memory) pair against the Fargate table.\n *\n * Deliberately strict rather than snapping to the nearest legal value: silently\n * giving an app 2 GB when its owner asked for 1.5 GB means the bill and the\n * behaviour do not match what was requested, and a size that is quietly changed\n * is a size nobody can reason about when the app OOMs anyway.\n */\nexport function validateSize(cpu: number, memory: number): SizeValidation {\n if (!FARGATE_SIZES[cpu]) {\n return {\n ok: false,\n error: `cpu must be one of ${FARGATE_CPUS.join(\", \")} (got ${cpu})`,\n };\n }\n const allowed = legalMemory(cpu);\n if (!allowed.includes(memory)) {\n // Long lists are unhelpful in an error message; show the shape instead.\n const shown =\n allowed.length <= 6\n ? allowed.join(\", \")\n : `${allowed[0]}\u2013${allowed[allowed.length - 1]} in steps of ${allowed[1] - allowed[0]}`;\n return {\n ok: false,\n error: `memory ${memory} is not valid for cpu ${cpu}; allowed: ${shown}`,\n allowed,\n };\n }\n return { ok: true };\n}\n\nexport interface TaskSize {\n cpu: number;\n memory: number;\n}\n\n/**\n * The size an app gets when nobody chose one.\n *\n * Picked per runtime because the failure is runtime-specific: `next start` holds\n * a compiled server plus its module graph and does not fit in 512 MB, while a\n * Go binary is comfortable in it. Defaulting everything to the larger size would\n * quadruple the bill for the runtimes that do not need it; defaulting everything\n * to the smaller one is the bug this replaces.\n */\nexport function defaultSizeFor(runtime: Runtime): TaskSize {\n switch (runtime) {\n case \"nextjs\":\n return { cpu: 512, memory: 1024 };\n case \"nodejs\":\n case \"python\":\n // Comfortable for a framework plus dependencies, and the smallest legal\n // pair above the old default.\n return { cpu: 256, memory: 1024 };\n case \"go\":\n return { cpu: 256, memory: 512 };\n case \"custom\":\n // Unknown contents, so assume it is doing something real.\n return { cpu: 512, memory: 1024 };\n }\n}\n\n/**\n * How many tasks a size change or a scale-out may run at once.\n *\n * A bound exists because `instances` is caller-supplied and reaches RunTask\n * directly: a typo of 100 would launch a hundred Fargate tasks and bill for them\n * before anyone noticed. RunTask itself caps a single call at 10.\n */\nexport const MAX_INSTANCES = 10;\n", "/**\n * What a plan allows and what usage costs.\n *\n * Kept here rather than in the control API because three consumers need the same\n * numbers and must not disagree about them: the API enforces the limits, the\n * usage collector prices the metrics it records, and the CLI prints a bill. A\n * rate duplicated in two of those places is a bill that does not match the\n * invoice.\n *\n * Two things this deliberately is *not*:\n *\n * - **A payment integration.** Nothing here charges anyone. It computes what an\n * org owes; collecting it needs a payment processor and a human to accept its\n * terms, the same way SES production access does.\n * - **The authority on AWS cost.** These are the platform's *prices*. The real\n * cost of a Fargate task is in Cost Explorer, and the two will not match --\n * that difference is the margin, and pretending otherwise would make every\n * price change look like a metering bug.\n */\n\n/**\n * Metrics recorded in `usage_records`.\n *\n * `vcpu_seconds` and `gb_seconds` are what Fargate actually charges for, so they\n * are what a compute line is priced from; `task_seconds` is recorded alongside\n * them because it is the number a human recognises (\"this ran for 20 hours\") and\n * cannot be recovered from the other two without knowing the task size.\n *\n * `storage_mb`, not bytes: `usage_records.quantity` is an `integer`, and bytes\n * overflows int32 at 2.1 GB -- silently, wrapping negative, which would read as a\n * credit.\n *\n * `lambda_gb_seconds` and `lambda_invocations` are the serverless compute line,\n * deliberately *not* folded into `gb_seconds`. They price differently, they come\n * from a different source (CloudWatch's own measurement rather than arithmetic\n * over a deployment row), and an app that switched modes mid-month has to produce\n * an invoice a person can audit -- two lines that each say where they came from,\n * not one line that silently mixes two meters.\n */\nexport type UsageMetric =\n | \"task_seconds\"\n | \"vcpu_seconds\"\n | \"gb_seconds\"\n | \"requests\"\n | \"build_seconds\"\n | \"storage_mb\"\n | \"emails_sent\"\n | \"db_days\"\n | \"lambda_gb_seconds\"\n | \"lambda_invocations\";\n\nexport const USAGE_METRICS: UsageMetric[] = [\n \"task_seconds\",\n \"vcpu_seconds\",\n \"gb_seconds\",\n \"requests\",\n \"build_seconds\",\n \"storage_mb\",\n \"emails_sent\",\n \"db_days\",\n \"lambda_gb_seconds\",\n \"lambda_invocations\",\n];\n\n/**\n * Metrics that are a level rather than a total.\n *\n * A day's `storage_mb` row is the last sample taken that day, not a sum of\n * samples -- summing a level double-counts every byte for every time it was\n * observed. Billing a level means averaging the samples across the period, which\n * is why the two kinds cannot be aggregated by the same code path.\n */\nexport const GAUGE_METRICS: UsageMetric[] = [\"storage_mb\"];\n\nexport function isGauge(metric: string): boolean {\n return (GAUGE_METRICS as string[]).includes(metric);\n}\n\n/**\n * Price per unit, in cents. Fractional on purpose: a request costs a fifth of a\n * millionth of a cent and rounding each one to a cent would bill $10,000 for a\n * million requests.\n *\n * `task_seconds` is priced at zero because compute is already billed through\n * `vcpu_seconds` and `gb_seconds`. It is in the table rather than absent from it\n * so that a new metric added without a price is a visible zero here instead of an\n * `undefined` multiplied into `NaN` further down.\n */\nexport const RATES: Record<UsageMetric, number> = {\n task_seconds: 0,\n // Fargate on-demand in us-east-1 is ~$0.04048/vCPU-hour and ~$0.004445/GB-hour.\n vcpu_seconds: 0.002,\n gb_seconds: 0.00025,\n // $0.20 per million requests.\n requests: 0.00002,\n // ~$0.012 per build-minute.\n build_seconds: 0.02,\n // $0.05 per GB-month, charged per MB-month.\n storage_mb: 0.005,\n // $0.50 per thousand.\n emails_sent: 0.05,\n // ~$3/month for a managed schema, charged per day it exists.\n db_days: 10,\n // Lambda list price is $0.0000166667 per GB-second; this carries roughly the\n // same markup over list as the Fargate rates above, so the two compute lines\n // are comparable and a mode switch does not look like a price change.\n lambda_gb_seconds: 0.0033,\n // Lambda's own $0.20 per million invocations, passed through at list. The\n // platform's markup on a request is already collected once by the `requests`\n // line at the edge, and one visit is one event -- marking it up on both meters\n // would charge twice for the same request.\n lambda_invocations: 0.00002,\n};\n\n/** Human unit for each metric, for a bill a person has to read. */\nexport const METRIC_LABELS: Record<UsageMetric, string> = {\n task_seconds: \"Task-seconds served\",\n vcpu_seconds: \"vCPU-seconds\",\n gb_seconds: \"GB-seconds\",\n requests: \"Requests\",\n build_seconds: \"Build seconds\",\n storage_mb: \"Object storage (MB-month)\",\n emails_sent: \"Emails sent\",\n db_days: \"Managed Postgres (app-days)\",\n lambda_gb_seconds: \"Serverless GB-seconds\",\n lambda_invocations: \"Serverless invocations\",\n};\n\nexport interface Plan {\n id: string;\n name: string;\n /** Monthly subscription, in cents, before usage. */\n baseCents: number;\n /**\n * Usage covered by the base fee, in cents. An org that stays under this pays\n * exactly `baseCents` -- so the free plan is a real free tier rather than a\n * $0.03 invoice for a weekend project.\n */\n includedUsageCents: number;\n /** Apps that are not soft-deleted. */\n maxApps: number;\n maxInstancesPerApp: number;\n /** Ceilings on Fargate task size, which is the one knob that scales the bill. */\n maxCpu: number;\n maxMemory: number;\n /** Object-storage quota, enforced on upload. */\n maxStorageMb: number;\n postgres: boolean;\n customDomains: boolean;\n}\n\n/**\n * The plans.\n *\n * The limits that exist are the ones that bound *spend*: app count, instance\n * count and task size. There is deliberately no limit on requests, builds or\n * deploys -- an agent-built app that gets popular should get a bill, not an\n * outage, and a platform whose selling point is \"push and it is live\" cannot\n * answer a deploy with \"not on your plan\".\n */\nexport const PLANS: Record<string, Plan> = {\n free: {\n id: \"free\",\n name: \"Free\",\n baseCents: 0,\n includedUsageCents: 300,\n maxApps: 3,\n maxInstancesPerApp: 1,\n // One app at this size, running all month, is inside the included usage.\n maxCpu: 512,\n maxMemory: 1024,\n maxStorageMb: 1024,\n postgres: true,\n customDomains: false,\n },\n pro: {\n id: \"pro\",\n name: \"Pro\",\n baseCents: 2000,\n includedUsageCents: 2000,\n maxApps: 50,\n maxInstancesPerApp: 4,\n maxCpu: 4096,\n maxMemory: 8192,\n maxStorageMb: 100 * 1024,\n postgres: true,\n customDomains: true,\n },\n scale: {\n id: \"scale\",\n name: \"Scale\",\n baseCents: 20000,\n includedUsageCents: 20000,\n maxApps: 500,\n maxInstancesPerApp: 10,\n maxCpu: 16384,\n maxMemory: 122880,\n maxStorageMb: 1024 * 1024,\n postgres: true,\n customDomains: true,\n },\n};\n\n/**\n * The plan for a stored `orgs.plan` value.\n *\n * Falls back to free rather than throwing: the column is free text and an\n * unrecognised value must not take an org's whole control surface down with a\n * 500. Free is the safe direction -- its limits are the tightest, so a typo\n * cannot silently grant a larger allowance than anyone agreed to.\n */\nexport function planFor(plan: string | null | undefined): Plan {\n return PLANS[plan || \"free\"] ?? PLANS.free;\n}\n\nexport interface UsageLine {\n metric: UsageMetric | string;\n label: string;\n quantity: number;\n unitPriceCents: number;\n /** Rounded to whole cents only at the line, never per unit. */\n cents: number;\n}\n\nexport interface Invoice {\n plan: string;\n periodStart: string;\n periodEnd: string;\n lines: UsageLine[];\n /** Everything the usage lines add up to, before the included allowance. */\n usageCents: number;\n baseCents: number;\n includedUsageCents: number;\n /** Usage beyond the allowance. Zero, never negative: credit does not roll over. */\n overageCents: number;\n totalCents: number;\n}\n\n/**\n * Prices a period's usage.\n *\n * `quantities` is metric \u2192 quantity, already aggregated over the period (sums for\n * counters, averages for gauges -- see `GAUGE_METRICS`, and note that this\n * function cannot tell which it was given, so aggregating wrongly produces a\n * plausible bill rather than an error).\n */\nexport function priceUsage(\n quantities: Record<string, number>,\n plan: Plan,\n period: { start: Date; end: Date },\n): Invoice {\n const lines: UsageLine[] = [];\n for (const metric of USAGE_METRICS) {\n const quantity = quantities[metric] ?? 0;\n const rate = RATES[metric];\n // A zero-priced metric is still reported when there is something to report:\n // task-seconds is how an owner checks that the compute line is plausible.\n if (quantity === 0) continue;\n lines.push({\n metric,\n label: METRIC_LABELS[metric],\n quantity: Math.round(quantity * 100) / 100,\n unitPriceCents: rate,\n cents: Math.round(quantity * rate),\n });\n }\n // Metrics recorded by a newer collector than this pricing table knows about.\n // Listed at zero rather than dropped: an unpriced metric that vanishes from the\n // bill is indistinguishable from one that was never collected.\n for (const metric of Object.keys(quantities)) {\n if ((USAGE_METRICS as string[]).includes(metric)) continue;\n if (!quantities[metric]) continue;\n lines.push({\n metric,\n label: metric,\n quantity: quantities[metric],\n unitPriceCents: 0,\n cents: 0,\n });\n }\n\n const usageCents = lines.reduce((acc, l) => acc + l.cents, 0);\n const overageCents = Math.max(0, usageCents - plan.includedUsageCents);\n return {\n plan: plan.id,\n periodStart: period.start.toISOString(),\n periodEnd: period.end.toISOString(),\n lines,\n usageCents,\n baseCents: plan.baseCents,\n includedUsageCents: plan.includedUsageCents,\n overageCents,\n totalCents: plan.baseCents + overageCents,\n };\n}\n\n/** `1234` \u2192 `\"$12.34\"`. For output a person reads, not for arithmetic. */\nexport function formatCents(cents: number): string {\n const sign = cents < 0 ? \"-\" : \"\";\n const abs = Math.abs(Math.round(cents));\n return `${sign}$${Math.floor(abs / 100)}.${String(abs % 100).padStart(2, \"0\")}`;\n}\n\nexport interface LimitCheck {\n ok: boolean;\n /** Names the limit and the plan, so the error says what to do about it. */\n error?: string;\n code?: string;\n}\n\nexport function allowed(): LimitCheck {\n return { ok: true };\n}\n\nfunction denied(code: string, error: string): LimitCheck {\n return { ok: false, error, code };\n}\n\n/** Whether one more app is within the plan. `current` excludes deleted apps. */\nexport function checkAppCount(plan: Plan, current: number): LimitCheck {\n if (current < plan.maxApps) return allowed();\n return denied(\n \"PLAN_LIMIT_APPS\",\n `The ${plan.name} plan allows ${plan.maxApps} app${plan.maxApps === 1 ? \"\" : \"s\"} ` +\n `and this org has ${current}. Delete one or upgrade the plan.`,\n );\n}\n\n/**\n * Whether a requested task shape is within the plan.\n *\n * Checked as a triple rather than three separate limits because they multiply:\n * the thing being bounded is the bill, and 4 instances of a small task costs the\n * same as one large one.\n */\nexport function checkTaskShape(\n plan: Plan,\n shape: { cpu?: number | null; memory?: number | null; instances?: number | null },\n): LimitCheck {\n if (shape.instances != null && shape.instances > plan.maxInstancesPerApp) {\n return denied(\n \"PLAN_LIMIT_INSTANCES\",\n `The ${plan.name} plan allows ${plan.maxInstancesPerApp} instance` +\n `${plan.maxInstancesPerApp === 1 ? \"\" : \"s\"} per app (asked for ${shape.instances}).`,\n );\n }\n if (shape.cpu != null && shape.cpu > plan.maxCpu) {\n return denied(\n \"PLAN_LIMIT_CPU\",\n `The ${plan.name} plan allows up to ${plan.maxCpu} CPU units per task (asked for ${shape.cpu}).`,\n );\n }\n if (shape.memory != null && shape.memory > plan.maxMemory) {\n return denied(\n \"PLAN_LIMIT_MEMORY\",\n `The ${plan.name} plan allows up to ${plan.maxMemory} MB per task (asked for ${shape.memory}).`,\n );\n }\n return allowed();\n}\n\n/** Whether a custom domain is within the plan. */\nexport function checkCustomDomain(plan: Plan): LimitCheck {\n if (plan.customDomains) return allowed();\n return denied(\n \"PLAN_LIMIT_DOMAINS\",\n `Custom domains are not included in the ${plan.name} plan. Every app keeps its ` +\n `platform hostname, which needs no configuration.`,\n );\n}\n", "import type {\n App,\n Snapshot,\n Deployment,\n Secret,\n Organization,\n OrgMember,\n LogEntry,\n LogPage,\n AppMetrics,\n CreateAppRequest,\n ApiResponse,\n Environment,\n AppHealth,\n BuildLogs,\n ApiKey,\n ApiKeyWithSecret,\n Job,\n OrgUsage,\n AppUsage,\n PlanInfo,\n AppMode,\n Visibility,\n} from \"@cloud/shared\";\nimport { DEFAULT_API_URL } from \"@cloud/shared\";\n\n/**\n * Only all-lowercase snake_case, which is what the API emits for its own fields.\n *\n * Deliberately narrow. Data the platform carries on behalf of a user also\n * travels through here -- secret names like `DATABASE_URL`, file paths like\n * `app/page.js` -- and rewriting one of those would corrupt it. Uppercase, dots\n * and slashes all fail this test, so those pass through untouched.\n */\nconst SNAKE_KEY = /^[a-z0-9]+(_[a-z0-9]+)+$/;\n\n/**\n * Fields whose *contents* belong to the caller, not to the API.\n *\n * A job's payload is a blob the app wrote and the app will read back; renaming\n * `order_id` inside it to `orderId` would corrupt the app's own data on its way\n * through the SDK. Same reasoning as the narrow key pattern above -- the key\n * itself is still converted, only what hangs off it is left alone.\n */\nconst OPAQUE_KEYS = new Set([\"payload\", \"files\", \"metadata\"]);\n\n/**\n * Recursively rewrites snake_case keys to camelCase.\n *\n * The API's wire format is snake_case (`created_at`, `key_prefix`); the SDK's\n * types are camelCase. Nothing used to bridge the two -- `response.json()` was\n * simply cast to the typed shape -- so every such field was `undefined` at\n * runtime while the compiler insisted it was a string. That is the worst\n * possible failure mode for a typed client: `deployment.createdAt` produced\n * `NaN` elapsed times and `key.keyPrefix` printed \"undefined\u2026\", with no error\n * anywhere. The conversion belongs here rather than in the API, which other\n * consumers already read as snake_case.\n */\nexport function camelizeDeep(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(camelizeDeep);\n if (value === null || typeof value !== \"object\") return value;\n // Dates survive JSON as strings, so anything object-shaped here is a plain\n // object from JSON.parse.\n const out: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value as Record<string, unknown>)) {\n const camel = SNAKE_KEY.test(key)\n ? key.replace(/_([a-z0-9])/g, (_, c: string) => c.toUpperCase())\n : key;\n out[camel] = OPAQUE_KEYS.has(camel) ? v : camelizeDeep(v);\n }\n return out;\n}\n\n/**\n * The type-level counterpart of `camelizeDeep`.\n *\n * Some shared types describe the *wire* shape and keep its snake_case (`LogPage`,\n * `BuildLogs`), because that is what a non-SDK consumer reading the API sees. The\n * SDK rewrites those keys at runtime, so returning the wire type from an SDK\n * method would reintroduce exactly the bug `camelizeDeep` was written to fix: a\n * field the compiler swears is a string and that is `undefined` at runtime. This\n * makes the declared type follow the conversion instead of contradicting it.\n */\ntype CamelKey<K extends string> = K extends `${infer A}_${infer B}`\n ? `${A}${Capitalize<CamelKey<B>>}`\n : K;\n\nexport type Camelize<T> = T extends (infer U)[]\n ? Camelize<U>[]\n : T extends object\n ? { [K in keyof T as K extends string ? CamelKey<K> : K]: Camelize<T[K]> }\n : T;\n\nexport class CloudError extends Error {\n public readonly code: string;\n public readonly status: number;\n public readonly details?: Record<string, unknown>;\n\n constructor(message: string, code: string, status: number, details?: Record<string, unknown>) {\n super(message);\n this.name = \"CloudError\";\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n\nexport interface CloudClientOptions {\n apiKey: string;\n baseUrl?: string;\n}\n\nexport class CloudClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n\n public readonly apps: AppsNamespace;\n public readonly secrets: SecretsNamespace;\n public readonly orgs: OrgsNamespace;\n public readonly apiKeys: ApiKeysNamespace;\n public readonly storage: StorageNamespace;\n public readonly email: EmailNamespace;\n public readonly jobs: JobsNamespace;\n\n constructor(options: CloudClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_API_URL).replace(/\\/$/, \"\");\n this.apps = new AppsNamespace(this);\n this.secrets = new SecretsNamespace(this);\n this.orgs = new OrgsNamespace(this);\n this.apiKeys = new ApiKeysNamespace(this);\n this.storage = new StorageNamespace(this);\n this.email = new EmailNamespace(this);\n this.jobs = new JobsNamespace(this);\n }\n\n /**\n * The plan catalogue. Unauthenticated on the API, so this works with any key --\n * including a placeholder one -- and can be called before an account exists to\n * show what an upgrade would allow.\n */\n async plans(): Promise<PlanInfo[]> {\n const res = await this.request<ApiResponse<PlanInfo[]>>(\"GET\", \"/plans\");\n return res.data;\n }\n\n async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const headers: Record<string, string> = {\n \"Authorization\": `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n\n // `fetch` reports every transport failure as the same bare `TypeError: fetch\n // failed`, with the cause one level down and the URL nowhere in it. A caller\n // seeing that cannot tell a DNS mistake from a reset connection from an API\n // that is down, which is the same class of unhelpfulness as the old\n // \"check your credentials\" for a 404. Status 0 says the request never got an\n // answer, so a retry is reasonable -- unlike a 4xx.\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n } catch (e: any) {\n const cause = e?.cause?.code || e?.cause?.message || e?.message || \"unknown error\";\n throw new CloudError(\n `Could not reach ${url} (${cause}).`,\n \"network_error\",\n 0,\n );\n }\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({}));\n const err = (errorBody as { error?: { code?: string; message?: string; details?: Record<string, unknown> } }).error;\n throw new CloudError(\n err?.message ?? `Request failed with status ${response.status}`,\n err?.code ?? \"unknown_error\",\n response.status,\n err?.details,\n );\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return camelizeDeep(await response.json()) as T;\n }\n}\n\nclass AppsNamespace {\n constructor(private client: CloudClient) {}\n\n async create(opts: {\n name: string;\n orgId: string;\n postgres?: boolean;\n /** Gives the app an object-storage prefix; see `client.storage`. */\n storage?: boolean;\n /** Lets the app send mail through the platform; see `client.email`. */\n email?: boolean;\n /**\n * Who may reach the running app: \"private\" | \"org\" | \"authenticated\" |\n * \"public\", narrowest first. Defaults to \"org\". Prefer this over\n * `authRequired`, which cannot express the difference between \"only the\n * people I invite\" and \"anyone in my org\".\n */\n visibility?: Visibility;\n /** @deprecated Use `visibility`. false is equivalent to \"public\". */\n authRequired?: boolean;\n /**\n * Groups the apps of one logical system under a shared label -- set the\n * same value on a split frontend and backend so the console shows them as\n * one stack. Slug-shaped: lowercase letters, digits and hyphens.\n */\n stack?: string;\n /**\n * How the app runs: \"container\" (a Fargate task, always on) or\n * \"serverless\" (a Lambda function that scales to zero and bills only for\n * requests it serves). Defaults to \"container\".\n *\n * Pick \"serverless\" for request/response HTTP -- a dashboard, an API, a\n * webhook receiver -- and \"container\" when the app needs cron, background\n * jobs, WebSockets, a custom Dockerfile, work longer than a minute, or\n * in-process state that must survive between requests. Either way the\n * contract is the same: listen on port 3000.\n */\n mode?: AppMode;\n }): Promise<App> {\n const body: CreateAppRequest = {\n name: opts.name,\n org_id: opts.orgId,\n postgres_enabled: opts.postgres,\n storage_enabled: opts.storage,\n email_enabled: opts.email,\n auth_required: opts.authRequired,\n visibility: opts.visibility,\n stack: opts.stack,\n mode: opts.mode,\n };\n const res = await this.client.request<ApiResponse<App>>(\"POST\", \"/apps\", body);\n return res.data;\n }\n\n /**\n * Changes an app's settings.\n *\n * `restartRequired` is true when the change only takes effect on a new\n * container -- size, scale and postgres. The API reports it rather than\n * restarting on its own: a restart is a brief interruption, and doing it as a\n * side effect of a settings write is not the caller's decision to discover\n * afterwards. Follow with `restart()` when it comes back true.\n */\n async update(\n appId: string,\n changes: {\n name?: string;\n /**\n * Who may reach the running app. Admin-level: an editor may deploy the\n * app but not change who can open it.\n *\n * Note that `authRequired: true` will not narrow an app that is already\n * gated -- the boolean cannot tell \"private\" from \"org\", so the API\n * leaves the tier alone rather than risk widening a private app. Send\n * `visibility` to move between the gated tiers.\n */\n visibility?: Visibility;\n /** @deprecated Use `visibility`. false is equivalent to \"public\". */\n authRequired?: boolean;\n /** \"active\" or \"paused\". */\n status?: \"active\" | \"paused\";\n cpu?: number;\n memory?: number;\n instances?: number;\n /** Requests per minute per client, or null to remove the limit. */\n rateLimitPerMinute?: number | null;\n postgres?: boolean;\n storage?: boolean;\n email?: boolean;\n /** Stack label grouping this app with its siblings, or null to ungroup. */\n stack?: string | null;\n /**\n * Switches between \"container\" and \"serverless\". Like a resize, the\n * change applies on the next deploy, start or restart rather than\n * immediately -- the running version keeps serving until then. Refused\n * while the app has cron schedules or background jobs, which serverless\n * mode does not run.\n */\n mode?: AppMode;\n },\n ): Promise<App & { restartRequired?: boolean }> {\n const body: Record<string, unknown> = {};\n if (changes.name !== undefined) body.name = changes.name;\n if (changes.authRequired !== undefined) body.auth_required = changes.authRequired;\n if (changes.visibility !== undefined) body.visibility = changes.visibility;\n if (changes.status !== undefined) body.status = changes.status;\n if (changes.cpu !== undefined) body.cpu = changes.cpu;\n if (changes.memory !== undefined) body.memory = changes.memory;\n if (changes.instances !== undefined) body.instances = changes.instances;\n // Sent when explicitly null, which is how a limit is removed -- omitting it\n // would leave the old limit in place and look like the call did nothing.\n if (changes.rateLimitPerMinute !== undefined) {\n body.rate_limit_per_minute = changes.rateLimitPerMinute;\n }\n if (changes.postgres !== undefined) body.postgres_enabled = changes.postgres;\n if (changes.storage !== undefined) body.storage_enabled = changes.storage;\n if (changes.email !== undefined) body.email_enabled = changes.email;\n // Like the rate limit: explicit null ungroups, omitted leaves it alone.\n if (changes.stack !== undefined) body.stack = changes.stack;\n if (changes.mode !== undefined) body.mode = changes.mode;\n\n const res = await this.client.request<ApiResponse<App & { restartRequired?: boolean }>>(\n \"PATCH\",\n `/apps/${appId}`,\n body,\n );\n return res.data;\n }\n\n async list(orgId: string): Promise<App[]> {\n const res = await this.client.request<ApiResponse<App[]>>(\n \"GET\",\n `/apps?org_id=${encodeURIComponent(orgId)}`,\n );\n return res.data;\n }\n\n async get(id: string): Promise<App> {\n const res = await this.client.request<ApiResponse<App>>(\"GET\", `/apps/${id}`);\n return res.data;\n }\n\n /**\n * Deletes the app and tears down its AWS resources.\n *\n * Teardown is fault-isolated per resource, so a partial failure still deletes\n * the app and reports what leaked in `warnings` (HTTP 207) rather than\n * aborting. An empty array means everything was released.\n */\n async delete(id: string): Promise<{ warnings: { step: string; error?: string }[] }> {\n const res = await this.client.request<{\n data: App;\n warnings?: { step: string; error?: string }[];\n }>(\"DELETE\", `/apps/${id}`);\n return { warnings: res?.warnings ?? [] };\n }\n\n /**\n * Pushes a snapshot.\n *\n * `files` carries text. Anything that is not valid UTF-8 \u2014 an image, a\n * favicon, a compiled binary \u2014 must go in `binaryFiles` as base64, because\n * JSON cannot carry arbitrary bytes in a string and a value that is silently\n * re-encoded arrives corrupted. The two are separate maps rather than one\n * with a flag: base64 is not detectable (`\"data\"` is valid base64 and valid\n * text), so the encoding has to be stated by the caller.\n */\n async push(\n appId: string,\n files: Record<string, string>,\n message?: string,\n binaryFiles?: Record<string, string>,\n ): Promise<Snapshot> {\n const res = await this.client.request<ApiResponse<Snapshot>>(\"POST\", `/apps/${appId}/push`, {\n files,\n message,\n // Request bodies are not camelized \u2014 only responses are \u2014 so the wire\n // name has to be written out here.\n ...(binaryFiles && Object.keys(binaryFiles).length > 0 ? { binary_files: binaryFiles } : {}),\n });\n return res.data;\n }\n\n async deploy(appId: string, snapshotId: string, environment?: Environment): Promise<Deployment> {\n const res = await this.client.request<ApiResponse<Deployment>>(\"POST\", `/apps/${appId}/deploy`, {\n snapshot_id: snapshotId,\n environment,\n });\n return res.data;\n }\n\n async promote(deploymentId: string): Promise<Deployment> {\n const res = await this.client.request<ApiResponse<Deployment>>(\"POST\", `/deployments/${deploymentId}/promote`, {});\n return res.data;\n }\n\n async rollback(appId: string): Promise<Deployment> {\n const res = await this.client.request<ApiResponse<Deployment>>(\"POST\", `/apps/${appId}/rollback`, {});\n return res.data;\n }\n\n async checkDeployment(deploymentId: string): Promise<Deployment> {\n const res = await this.client.request<ApiResponse<Deployment>>(\"POST\", `/deployments/${deploymentId}/status`, {});\n return res.data;\n }\n\n /**\n * A page of the app's logs, newest last.\n *\n * Returns a page rather than a bare array of entries, which is what this used to\n * be: an array cannot say whether there is more to fetch, and the endpoint\n * behind it silently answered with the newest few log streams as though they\n * were the whole app. To page, pass the previous response's `nextToken`; to\n * follow, pass the last event's `timestamp` as `since`.\n *\n * `since`/`until` accept an ISO timestamp, epoch millis, or an offset like\n * `15m`. `q` is a literal substring; `pattern` is CloudWatch filter syntax --\n * pass one or the other, not both.\n */\n async logs(\n appId: string,\n opts?: {\n limit?: number;\n since?: string;\n until?: string;\n q?: string;\n pattern?: string;\n /** Log stream prefix, to read one instance instead of all of them. */\n instance?: string;\n nextToken?: string;\n },\n ): Promise<Camelize<LogPage>> {\n const params = new URLSearchParams();\n if (opts?.limit) params.set(\"limit\", String(opts.limit));\n if (opts?.since) params.set(\"since\", opts.since);\n if (opts?.until) params.set(\"until\", opts.until);\n if (opts?.q) params.set(\"q\", opts.q);\n if (opts?.pattern) params.set(\"pattern\", opts.pattern);\n if (opts?.instance) params.set(\"instance\", opts.instance);\n if (opts?.nextToken) params.set(\"next_token\", opts.nextToken);\n const query = params.toString();\n const path = `/apps/${appId}/logs${query ? `?${query}` : \"\"}`;\n const res = await this.client.request<ApiResponse<Camelize<LogPage>>>(\"GET\", path);\n return res.data;\n }\n\n /**\n * Follows the app's logs, yielding each line as it appears.\n *\n * Polling rather than the API's SSE tail: an async generator works in node\n * without `EventSource`, and it carries the bearer token the same way as every\n * other call. The cursor is the last event's timestamp and the window is\n * reopened *at* it rather than after it -- several lines share a millisecond, so\n * advancing past it would drop the rest of them -- with the boundary\n * millisecond's lines remembered to suppress the repeat.\n */\n async *followLogs(\n appId: string,\n opts?: { since?: string; q?: string; pattern?: string; instance?: string; intervalMs?: number },\n ): AsyncGenerator<LogEntry> {\n const interval = opts?.intervalMs ?? 2000;\n let since = opts?.since ?? \"60s\";\n let seenAtCursor = new Set<string>();\n\n while (true) {\n const page = await this.logs(appId, {\n since,\n limit: 500,\n q: opts?.q,\n pattern: opts?.pattern,\n instance: opts?.instance,\n });\n\n for (const event of page.events) {\n const key = `${event.stream || \"\"} ${event.message}`;\n if (event.timestamp === since) {\n if (seenAtCursor.has(key)) continue;\n seenAtCursor.add(key);\n } else {\n since = event.timestamp;\n seenAtCursor = new Set([key]);\n }\n yield event;\n }\n\n await new Promise((r) => setTimeout(r, interval));\n }\n }\n\n /**\n * Traffic, latency and resource use over a window.\n *\n * `since`/`until` take the same values as `logs`, deliberately: an owner who\n * spots a spike here reads the logs for the same period next.\n *\n * Note the series keys arrive camelized like every other field on this client,\n * so the API's `client_errors` is `clientErrors` and `latency_p95` is\n * `latencyP95`.\n */\n async metrics(\n appId: string,\n opts?: { since?: string; until?: string },\n ): Promise<Camelize<AppMetrics>> {\n const params = new URLSearchParams();\n if (opts?.since) params.set(\"since\", opts.since);\n if (opts?.until) params.set(\"until\", opts.until);\n const query = params.toString();\n const res = await this.client.request<ApiResponse<Camelize<AppMetrics>>>(\n \"GET\",\n `/apps/${appId}/metrics${query ? `?${query}` : \"\"}`,\n );\n return res.data;\n }\n\n /**\n * Live runtime state, including the container's exit code when it has crashed.\n * A deployment stays \"running\" in the database after the task dies, so this is\n * the only way to tell a healthy app from a crash loop.\n */\n /**\n * What this app cost in a month (`YYYY-MM`, default the month in progress),\n * priced at list rates with no allowance applied -- the plan's included usage\n * belongs to the org, so per-app figures would otherwise sum past the invoice.\n */\n async usage(appId: string, period?: string): Promise<AppUsage> {\n const query = period ? `?period=${encodeURIComponent(period)}` : \"\";\n const res = await this.client.request<ApiResponse<AppUsage>>(\n \"GET\",\n `/apps/${appId}/usage${query}`,\n );\n return res.data;\n }\n\n async health(appId: string): Promise<AppHealth> {\n const res = await this.client.request<ApiResponse<AppHealth>>(\"GET\", `/apps/${appId}/health`);\n return res.data;\n }\n\n /**\n * Build output for a deployment. Poll with the previous response's `next_token`\n * to stream a build as it runs.\n */\n async buildLogs(deploymentId: string, nextToken?: string): Promise<Camelize<BuildLogs>> {\n const query = nextToken ? `?next_token=${encodeURIComponent(nextToken)}` : \"\";\n const res = await this.client.request<ApiResponse<Camelize<BuildLogs>>>(\n \"GET\",\n `/deployments/${deploymentId}/build-logs${query}`,\n );\n return res.data;\n }\n}\n\nclass ApiKeysNamespace {\n constructor(private client: CloudClient) {}\n\n /**\n * Creates a key. The plaintext `key` is present on this response only -- the\n * server stores a hash and cannot return it again.\n */\n async create(opts: {\n name: string;\n orgId: string;\n expiresInDays?: number;\n /**\n * Pass an app id for a **service token**: a credential pinned to that one app,\n * for code running inside it. It cannot reach another app, issue further keys,\n * list the org's keys or revoke anything -- which is what makes it safe to\n * ship inside a container. A key without this acts as its creator across\n * every app in the org, so do not put one in a deployed app.\n */\n appId?: string;\n }): Promise<ApiKeyWithSecret> {\n const res = await this.client.request<ApiResponse<ApiKeyWithSecret>>(\"POST\", \"/api-keys\", {\n name: opts.name,\n org_id: opts.orgId,\n expires_in_days: opts.expiresInDays,\n app_id: opts.appId,\n });\n return res.data;\n }\n\n async list(orgId: string): Promise<ApiKey[]> {\n const res = await this.client.request<ApiResponse<ApiKey[]>>(\n \"GET\",\n `/api-keys?org_id=${encodeURIComponent(orgId)}`,\n );\n return res.data;\n }\n\n async revoke(id: string): Promise<void> {\n await this.client.request<void>(\"DELETE\", `/api-keys/${id}`);\n }\n}\n\nclass SecretsNamespace {\n constructor(private client: CloudClient) {}\n\n async set(appId: string, key: string, value: string): Promise<Secret> {\n const res = await this.client.request<ApiResponse<Secret>>(\"POST\", `/apps/${appId}/secrets`, {\n key,\n value,\n });\n return res.data;\n }\n\n async list(appId: string): Promise<Secret[]> {\n const res = await this.client.request<ApiResponse<Secret[]>>(\"GET\", `/apps/${appId}/secrets`);\n return res.data;\n }\n\n async delete(appId: string, key: string): Promise<void> {\n await this.client.request<void>(\"DELETE\", `/apps/${appId}/secrets/${key}`);\n }\n}\n\nexport interface StoredObject {\n key: string;\n size: number;\n lastModified: string | null;\n etag: string | null;\n}\n\n/**\n * Object storage for an app.\n *\n * Bytes never pass through the control API: it hands out a presigned S3 URL and\n * the transfer goes direct. `upload` and `download` wrap that two-step for the\n * common case; `presign` is there for handing a URL to a browser so an upload\n * skips the app's own server entirely.\n *\n * Best reached with a **service token** (`apiKeys.create({ appId })`) from inside\n * the app, so the credential in the container cannot touch anything else.\n */\nclass StorageNamespace {\n constructor(private client: CloudClient) {}\n\n async list(\n appId: string,\n opts: { prefix?: string; limit?: number; cursor?: string } = {},\n ): Promise<{ objects: StoredObject[]; nextCursor: string | null }> {\n const q = new URLSearchParams();\n if (opts.prefix) q.set(\"prefix\", opts.prefix);\n if (opts.limit) q.set(\"limit\", String(opts.limit));\n if (opts.cursor) q.set(\"cursor\", opts.cursor);\n const query = q.toString() ? `?${q}` : \"\";\n const res = await this.client.request<\n ApiResponse<{ objects: StoredObject[]; nextCursor: string | null }>\n >(\"GET\", `/apps/${appId}/storage/objects${query}`);\n return res.data;\n }\n\n /** Every object under the prefix, paged through for you. */\n async listAll(appId: string, prefix?: string): Promise<StoredObject[]> {\n const all: StoredObject[] = [];\n let cursor: string | undefined;\n do {\n const page = await this.list(appId, { prefix, cursor, limit: 1000 });\n all.push(...page.objects);\n cursor = page.nextCursor ?? undefined;\n } while (cursor);\n return all;\n }\n\n async usage(appId: string): Promise<{ objects: number; bytes: number }> {\n const res = await this.client.request<ApiResponse<{ objects: number; bytes: number }>>(\n \"GET\",\n `/apps/${appId}/storage/usage`,\n );\n return res.data;\n }\n\n /** Metadata for one object, or null when it is not there. */\n async head(appId: string, key: string): Promise<StoredObject | null> {\n try {\n const res = await this.client.request<ApiResponse<StoredObject>>(\n \"GET\",\n `/apps/${appId}/storage/object?key=${encodeURIComponent(key)}`,\n );\n return res.data;\n } catch (e) {\n if (e instanceof CloudError && e.status === 404) return null;\n throw e;\n }\n }\n\n async delete(appId: string, key: string): Promise<void> {\n await this.client.request<ApiResponse<unknown>>(\n \"DELETE\",\n `/apps/${appId}/storage/object?key=${encodeURIComponent(key)}`,\n );\n }\n\n /**\n * A short-lived URL for one key and one method. Hand a `put` URL to a browser to\n * upload without the bytes touching the app, or a `get` URL to share a private\n * object for a few minutes. Treat it as a credential: anyone holding it has that\n * access until it expires.\n */\n async presign(\n appId: string,\n key: string,\n opts: {\n method?: \"put\" | \"get\";\n expiresIn?: number;\n contentType?: string;\n download?: string;\n } = {},\n ): Promise<{ url: string; method: string; key: string; expiresIn: number }> {\n const res = await this.client.request<\n ApiResponse<{ url: string; method: string; key: string; expiresIn: number }>\n >(\"POST\", `/apps/${appId}/storage/presign`, {\n key,\n method: opts.method ?? \"get\",\n expires_in: opts.expiresIn,\n content_type: opts.contentType,\n download: opts.download,\n });\n return res.data;\n }\n\n /**\n * Presigns and uploads in one call.\n *\n * `contentType` is signed into the URL, so it is sent on the PUT as well --\n * omitting it there would make S3 reject the signature with an opaque 403.\n */\n async upload(\n appId: string,\n key: string,\n body: Uint8Array | ArrayBuffer | string | Blob,\n opts: { contentType?: string } = {},\n ): Promise<StoredObject> {\n const signed = await this.presign(appId, key, {\n method: \"put\",\n contentType: opts.contentType,\n });\n const res = await fetch(signed.url, {\n method: \"PUT\",\n headers: opts.contentType ? { \"Content-Type\": opts.contentType } : undefined,\n body: body as BodyInit,\n });\n if (!res.ok) {\n // S3's own error is XML, not the platform's JSON envelope, so it is passed\n // through as text rather than parsed into a CloudError shape it does not have.\n throw new CloudError(\n `Upload failed with status ${res.status}: ${(await res.text()).slice(0, 300)}`,\n \"upload_failed\",\n res.status,\n );\n }\n return { key, size: 0, lastModified: null, etag: res.headers.get(\"etag\") };\n }\n\n /** Presigns and fetches. Returns the raw bytes; decode as the caller sees fit. */\n async download(appId: string, key: string): Promise<Uint8Array> {\n const signed = await this.presign(appId, key, { method: \"get\" });\n const res = await fetch(signed.url);\n if (!res.ok) {\n throw new CloudError(\n `Download failed with status ${res.status}`,\n res.status === 404 ? \"not_found\" : \"download_failed\",\n res.status,\n );\n }\n return new Uint8Array(await res.arrayBuffer());\n }\n}\n\nexport interface EmailQuota {\n sent: number;\n quota: number;\n remaining: number;\n /** The API sends `resets_at`; `camelizeDeep` renames it on the way in. */\n resetsAt: string;\n}\n\nexport interface SentEmail {\n messageId: string | null;\n to: string[];\n from: string;\n quota: EmailQuota;\n}\n\n/**\n * Transactional email sent by the platform on the app's behalf.\n *\n * There is no `from`: mail goes out as `app-<slug>@<platform domain>`, because an\n * app able to choose its own From could send a credible password-reset email as\n * the platform. Use `replyTo` to route replies to your own address.\n */\nclass EmailNamespace {\n constructor(private client: CloudClient) {}\n\n /**\n * Sends one message, to up to 50 recipients, as a single all-or-nothing send.\n *\n * Throws `CloudError` with status 429 once the app's daily quota is used up, and\n * 502 when the provider rejected the message -- the message text carries the\n * reason, which while the sending account is sandboxed is usually that the\n * recipient address is not verified.\n */\n async send(\n appId: string,\n message: {\n to: string | string[];\n subject: string;\n text?: string;\n html?: string;\n replyTo?: string;\n },\n ): Promise<SentEmail> {\n const res = await this.client.request<ApiResponse<SentEmail>>(\n \"POST\",\n `/apps/${appId}/email`,\n {\n to: message.to,\n subject: message.subject,\n text: message.text,\n html: message.html,\n reply_to: message.replyTo,\n },\n );\n return res.data;\n }\n\n /** Sends used today and what is left. Resets at UTC midnight. */\n async quota(appId: string): Promise<EmailQuota> {\n const res = await this.client.request<ApiResponse<EmailQuota>>(\n \"GET\",\n `/apps/${appId}/email/quota`,\n );\n return res.data;\n }\n}\n\n/**\n * Background jobs.\n *\n * A job is delivered as a POST to a path on the app itself, so the \"worker\" is a\n * route in the app the agent already wrote. Delivery is at least once -- retries\n * mean a handler can see the same job twice -- which is what `idempotencyKey`\n * on the enqueue side and the `x-videan-job-attempt` header on the receiving side\n * are for.\n */\nclass JobsNamespace {\n constructor(private client: CloudClient) {}\n\n /**\n * Enqueues a job.\n *\n * Pass `idempotencyKey` when the caller may retry this enqueue: a repeat returns\n * the job already queued, with `deduplicated: true`, instead of a second copy of\n * the work.\n */\n async enqueue(\n appId: string,\n options: {\n queue?: string;\n /** Path on the app the job is POSTed to. Defaults to `/jobs`. */\n path?: string;\n method?: \"POST\" | \"PUT\" | \"PATCH\";\n payload?: unknown;\n maxAttempts?: number;\n timeoutSeconds?: number;\n delaySeconds?: number;\n idempotencyKey?: string;\n } = {},\n ): Promise<Job> {\n const res = await this.client.request<ApiResponse<Job>>(\"POST\", `/apps/${appId}/jobs`, {\n queue: options.queue,\n path: options.path,\n method: options.method,\n payload: options.payload,\n max_attempts: options.maxAttempts,\n timeout_seconds: options.timeoutSeconds,\n delay_seconds: options.delaySeconds,\n idempotency_key: options.idempotencyKey,\n });\n return res.data;\n }\n\n /** A page of jobs, newest first, with the queue depth per status alongside. */\n async list(\n appId: string,\n options: { status?: string; queue?: string; limit?: number } = {},\n ): Promise<{ jobs: Job[]; counts: Record<string, number> }> {\n const params = new URLSearchParams();\n if (options.status) params.set(\"status\", options.status);\n if (options.queue) params.set(\"queue\", options.queue);\n if (options.limit) params.set(\"limit\", String(options.limit));\n const query = params.toString();\n const res = await this.client.request<ApiResponse<{ jobs: Job[]; counts: Record<string, number> }>>(\n \"GET\",\n `/apps/${appId}/jobs${query ? `?${query}` : \"\"}`,\n );\n return res.data;\n }\n\n async get(appId: string, jobId: string): Promise<Job> {\n const res = await this.client.request<ApiResponse<Job>>(\"GET\", `/apps/${appId}/jobs/${jobId}`);\n return res.data;\n }\n\n /** Runs a dead job again, from a clean attempt count. */\n async retry(appId: string, jobId: string): Promise<Job> {\n const res = await this.client.request<ApiResponse<Job>>(\n \"POST\",\n `/apps/${appId}/jobs/${jobId}/retry`,\n );\n return res.data;\n }\n\n /** Cancels a job that has not started. Throws 409 once it is running. */\n async cancel(appId: string, jobId: string): Promise<Job> {\n const res = await this.client.request<ApiResponse<Job>>(\n \"POST\",\n `/apps/${appId}/jobs/${jobId}/cancel`,\n );\n return res.data;\n }\n}\n\nclass OrgsNamespace {\n constructor(private client: CloudClient) {}\n\n async list(): Promise<Organization[]> {\n const res = await this.client.request<ApiResponse<Organization[]>>(\"GET\", \"/orgs\");\n return res.data;\n }\n\n async create(name: string): Promise<Organization> {\n const res = await this.client.request<ApiResponse<Organization>>(\"POST\", \"/orgs\", { name });\n return res.data;\n }\n\n async get(id: string): Promise<Organization> {\n const res = await this.client.request<ApiResponse<Organization>>(\"GET\", `/orgs/${id}`);\n return res.data;\n }\n\n async members(id: string): Promise<OrgMember[]> {\n const res = await this.client.request<ApiResponse<OrgMember[]>>(\"GET\", `/orgs/${id}/members`);\n return res.data;\n }\n\n async invite(id: string, email: string, role: \"admin\" | \"member\" = \"member\"): Promise<void> {\n await this.client.request<ApiResponse<unknown>>(\"POST\", `/orgs/${id}/members`, { email, role });\n }\n\n async removeMember(id: string, userId: string): Promise<void> {\n await this.client.request<ApiResponse<unknown>>(\"DELETE\", `/orgs/${id}/members/${userId}`);\n }\n\n /**\n * Metered usage and the priced invoice for a month (`YYYY-MM`, default the month\n * in progress). Read-only: nothing in the SDK changes a plan or takes a payment.\n */\n async usage(id: string, period?: string): Promise<OrgUsage> {\n const query = period ? `?period=${encodeURIComponent(period)}` : \"\";\n const res = await this.client.request<ApiResponse<OrgUsage>>(\n \"GET\",\n `/orgs/${id}/usage${query}`,\n );\n return res.data;\n }\n}\n", "import type { Invoice, UsageLine } from \"./pricing.js\";\n\n// --- Enums ---\n\nexport enum DeploymentStatus {\n Building = \"building\",\n Deploying = \"deploying\",\n Running = \"running\",\n Failed = \"failed\",\n Stopped = \"stopped\",\n /**\n * Was serving, then its task died on its own -- OOM, a panic, an ECS\n * replacement. Distinct from Failed, which means the deploy never worked at\n * all: the difference is what the owner should do about it, and only one of\n * the two has an image known to have booted successfully.\n */\n Crashed = \"crashed\",\n}\n\nexport enum Environment {\n Preview = \"preview\",\n Production = \"production\",\n}\n\n/**\n * Which compute an app runs on.\n *\n * Both modes meet the same one contract -- serve HTTP on port 3000, behind the\n * same edge gate, with the same snapshot/promote/rollback semantics -- so this\n * is a choice about economics and constraints, not about how an app is written:\n *\n * - `container`: an ECS Fargate task. Always on, billed by the second, and the\n * only mode that can hold a WebSocket, run work longer than 15 minutes, keep\n * in-process state between requests, or build from a custom Dockerfile.\n * - `serverless`: an AWS Lambda container image behind the Lambda Web Adapter.\n * Scales to zero and bills per GB-ms, at the cost of a cold start on the\n * first request after idle. Request/response HTTP only.\n *\n * The agent picks explicitly. There is deliberately no auto-detection: a mode\n * guessed from the files is a mode that can silently differ from what was asked\n * for, which is the exact class of surprise the agent guide exists to prevent.\n * An omitted mode is `container` -- what every app was before the field existed.\n * A *misspelled* one is a 400, not a default.\n *\n * `as const` rather than an annotated array, so zod can take this list directly\n * as an enum -- `z.enum` needs a non-empty tuple type, which `AppMode[]` is not --\n * and the type is derived from the values, the way `VISIBILITIES` does it, so\n * there is one list and nothing to keep in step by hand.\n */\nexport const APP_MODES = [\"container\", \"serverless\"] as const;\n\nexport type AppMode = (typeof APP_MODES)[number];\n\n/**\n * Narrows a mode that crossed a boundary as `string` -- the column is text, so a\n * stored value is only a claim.\n *\n * Note what this deliberately does not do: it does not coerce. Readers fall back\n * to `container` for an unrecognised value, because that is the mode that\n * predates the column and cannot surprise anyone; the write path rejects instead.\n */\nexport function isAppMode(value: string): value is AppMode {\n return (APP_MODES as readonly string[]).includes(value);\n}\n\n/** The stored mode of an app or deployment, defaulted for rows that predate it. */\nexport function appModeOf(value: string | null | undefined): AppMode {\n return value && isAppMode(value) ? value : \"container\";\n}\n\nexport enum Role {\n Admin = \"admin\",\n Member = \"member\",\n}\n\n// --- Core entities ---\n\nexport interface App {\n id: string;\n name: string;\n slug: string;\n orgId: string;\n status: string;\n postgresEnabled: boolean;\n /** Whether the app has an object-storage prefix it can read and write. */\n storageEnabled?: boolean;\n /** Whether the app may send mail through the platform's verified identity. */\n emailEnabled?: boolean;\n /**\n * Legacy view of `visibility`: false only for the `public` tier. Kept because\n * an already-deployed edge router reads it and nothing else.\n */\n authRequired: boolean;\n /** Who may reach the running app. See `Visibility` in `visibility.ts`. */\n visibility?: string;\n /** ECS task size per instance. Null means the platform default for the runtime. */\n cpu?: number | null;\n memory?: number | null;\n instances?: number;\n /** Requests per minute per client, enforced at the edge. Null means unlimited. */\n rateLimitPerMinute?: number | null;\n /**\n * Groups the apps of one logical system (a split frontend + backend) under a\n * shared label. Purely descriptive -- the console clusters same-stack apps in\n * the list; nothing in routing, auth or billing reads it. Null = ungrouped.\n */\n stack?: string | null;\n /** Which compute this app runs on. See `AppMode`. */\n mode?: string;\n customDomain?: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface Snapshot {\n id: string;\n appId: string;\n files: Record<string, string>;\n createdAt: string;\n message?: string;\n}\n\nexport interface Deployment {\n id: string;\n appId: string;\n snapshotId: string;\n status: string;\n environment: string;\n /** Only on the response to the deploy that started it; not listed afterwards. */\n buildId?: string | null;\n /** The app's hostname, once this deployment is the promoted one. */\n url?: string | null;\n /**\n * This deployment's own hostname, live from the moment its tasks are up --\n * before it is promoted. What an agent checks its own work against without\n * production riding on the answer.\n */\n previewUrl?: string | null;\n imageTag?: string | null;\n /** The execution mode this deployment ran under. Absent means `container`. */\n mode?: string | null;\n /** Serverless only: the Lambda version this deployment pinned. */\n functionVersion?: string | null;\n /** Machine-readable failure cause, for a caller deciding what to fix. */\n failureReason?: string | null;\n failureDetail?: unknown;\n restartCount?: number;\n lastRestartAt?: string | null;\n createdAt: string;\n updatedAt?: string | null;\n completedAt?: string | null;\n error?: string;\n}\n\n/**\n * One background job.\n *\n * `status` is `queued` | `running` | `succeeded` | `dead` | `cancelled`. A failed\n * attempt with retries left is back in `queued` with `attempts` advanced, `error`\n * holding what went wrong last and `runAt` saying when it will be tried again --\n * there is no separate `failed` state, because a retriable failure and a pending\n * job are the same thing waiting.\n */\nexport interface Job {\n id: string;\n queue: string;\n path: string;\n method: string;\n payload: unknown;\n status: string;\n attempts: number;\n maxAttempts: number;\n runAt: string;\n timeoutSeconds: number;\n /** The handler's HTTP status on the last attempt. Null if it never answered. */\n statusCode?: number | null;\n error?: string | null;\n durationMs?: number | null;\n idempotencyKey?: string | null;\n createdAt: string;\n startedAt?: string | null;\n finishedAt?: string | null;\n /**\n * True when this enqueue matched an idempotency key already used, so the job\n * returned is the earlier one and nothing was created.\n */\n deduplicated?: boolean;\n}\n\n/**\n * One metered quantity. A list rather than a `metric -> quantity` map because\n * metric names are snake_case, and the SDK rewrites snake_case *keys* to camelCase\n * while leaving values alone -- as a key, `task_seconds` would arrive spelled\n * differently than the same name inside an invoice line.\n */\nexport interface UsageQuantity {\n metric: string;\n quantity: number;\n}\n\nexport interface AppUsageShare {\n /** Null for usage recorded by apps that have since been deleted. */\n appId: string | null;\n slug?: string;\n name?: string;\n metrics: UsageQuantity[];\n /** This app's share at list price, with no allowance applied. */\n cents: number;\n}\n\nexport interface OrgUsage {\n orgId: string;\n plan: string;\n periodStart: string;\n periodEnd: string;\n metrics: UsageQuantity[];\n apps: AppUsageShare[];\n invoice: Invoice;\n /** `invoice.totalCents` formatted, so a terminal never divides by 100 itself. */\n total: string;\n}\n\nexport interface AppUsage {\n appId: string;\n orgId: string;\n periodStart: string;\n periodEnd: string;\n metrics: UsageQuantity[];\n lines: UsageLine[];\n cents: number;\n cost: string;\n /** Per-day rows, newest first, so a spike is attributable to a day. */\n daily: Array<{ date: string; metric: string; quantity: number }>;\n}\n\n/** One entry of the public plan catalogue (`GET /v1/plans`). */\nexport interface PlanInfo {\n id: string;\n name: string;\n baseCents: number;\n base: string;\n includedUsageCents: number;\n includedUsage: string;\n limits: {\n apps: number;\n instancesPerApp: number;\n maxCpu: number;\n maxMemory: number;\n maxStorageMb: number;\n postgres: boolean;\n customDomains: boolean;\n };\n}\n\nexport interface Secret {\n key: string;\n appId: string;\n createdAt: string;\n updatedAt: string;\n}\n\n/** Live ECS state for an app's current deployment. */\nexport interface AppHealth {\n app_id: string;\n healthy: boolean;\n /** \"running\" | \"stopped\" | \"pending\" | \"building\" | \"not_deployed\" | \"no_task\" | \"task_gone\" */\n state: string;\n deployment_id: string | null;\n url: string | null;\n task: {\n status: string;\n desired_status?: string;\n health: string;\n ip?: string;\n port: number;\n stopped_reason?: string;\n stop_code?: string;\n exit_code?: number;\n container_reason?: string;\n started_at?: string;\n stopped_at?: string;\n } | null;\n}\n\nexport interface BuildPhase {\n name: string;\n status?: string;\n durationSeconds?: number;\n}\n\nexport interface BuildLogs {\n build_id: string;\n build_status: string;\n deployment_status: string;\n phases: BuildPhase[];\n lines: { timestamp: string; message: string }[];\n /** Pass back as `next_token` to fetch only what has been appended since. */\n next_token?: string;\n}\n\nexport interface ApiKey {\n id: string;\n name: string;\n orgId: string;\n keyPrefix: string;\n /**\n * `service` is pinned to `appId` and refused by everything outside that app;\n * `user` acts as its creator across the whole org.\n */\n kind?: \"user\" | \"service\";\n appId?: string | null;\n lastUsedAt?: string | null;\n expiresAt?: string | null;\n revokedAt?: string | null;\n createdAt: string;\n}\n\n/** Returned only by create -- `key` is never retrievable again. */\nexport interface ApiKeyWithSecret extends ApiKey {\n key: string;\n}\n\nexport interface Organization {\n id: string;\n name: string;\n slug: string;\n plan: string;\n createdAt: string;\n}\n\nexport interface User {\n id: string;\n email: string;\n name: string;\n avatarUrl?: string;\n createdAt: string;\n}\n\n/** Shape returned by GET /v1/orgs/:id/members -- flattened, not a nested user. */\nexport interface OrgMember {\n user_id: string;\n role: string;\n joined_at: string;\n email?: string;\n name?: string;\n}\n\n// --- API request types ---\n\nexport interface CreateAppRequest {\n name: string;\n org_id: string;\n postgres_enabled?: boolean;\n storage_enabled?: boolean;\n email_enabled?: boolean;\n auth_required?: boolean;\n /** One of `VISIBILITIES`. Wins over `auth_required` when both are sent. */\n visibility?: string;\n /** Stack label grouping this app with its siblings (slug-shaped, \u226450 chars). */\n stack?: string;\n /** One of `APP_MODES`. Omitted means `container`; a bad value is a 400. */\n mode?: AppMode;\n}\n\nexport interface PushRequest {\n /** Path \u2192 UTF-8 text. */\n files: Record<string, string>;\n /**\n * Path \u2192 base64. Required for anything that is not valid UTF-8, since JSON\n * strings cannot carry arbitrary bytes. Separate from `files` because base64\n * is not detectable \u2014 `\"data\"` is both \u2014 so the caller has to declare it.\n */\n binary_files?: Record<string, string>;\n message?: string;\n}\n\nexport interface DeployRequest {\n snapshot_id: string;\n environment?: Environment;\n}\n\nexport interface PromoteRequest {\n deployment_id: string;\n}\n\nexport interface RollbackRequest {\n app_id: string;\n}\n\nexport interface SetSecretRequest {\n key: string;\n value: string;\n}\n\nexport interface DeleteSecretRequest {\n key: string;\n}\n\nexport interface InviteMemberRequest {\n email: string;\n role?: Role;\n}\n\nexport interface GetLogsRequest {\n app_id: string;\n follow?: boolean;\n since?: string;\n limit?: number;\n}\n\n// --- API response types ---\n\nexport interface ApiResponse<T> {\n data: T;\n meta?: {\n total?: number;\n page?: number;\n per_page?: number;\n };\n}\n\nexport interface ApiError {\n error: {\n code: string;\n message: string;\n details?: Record<string, unknown>;\n };\n}\n\nexport interface LogEntry {\n timestamp: string;\n /**\n * Inferred from the text of the line, not read from structured output: the\n * contract with an app is \"expose a port\", so nothing guarantees it logs levels\n * at all. Useful for colouring a viewer; not something to filter on -- search is\n * the filter that tells the truth.\n */\n level: string;\n message: string;\n /**\n * Log stream, which identifies the *instance* that produced the line. With an\n * app running several tasks behind one hostname this is the only way to tell\n * whether one container is misbehaving or all of them are.\n */\n stream?: string;\n source?: string;\n}\n\n/**\n * One page of an app's logs.\n *\n * A page rather than a bare array because there are two things a bare array\n * cannot say: that there is more to fetch, and that the scan stopped early. Both\n * were previously invisible -- the old endpoint read the newest few log streams\n * and returned them as though they were the whole app.\n */\nexport interface LogPage {\n events: LogEntry[];\n /** Pass back as `next_token` to continue forward through the same window. */\n next_token?: string;\n /** The page budget was reached before the end of the window. */\n truncated: boolean;\n /** The window actually searched, which may not be the one requested verbatim. */\n start: string;\n end?: string;\n}\n\n/** One bucket of one series. `t` is the bucket's start, not its end. */\nexport interface MetricPoint {\n t: string;\n v: number;\n}\n\n/**\n * An app's metrics over a window.\n *\n * `series` is keyed by metric id: `requests`, `client_errors`, `server_errors`,\n * `gate_denied` and `latency_avg`/`latency_p95` come from the edge router (the only\n * component that sees per-app traffic); `cpu` and `memory` come from ECS Container\n * Insights, in CPU units and megabytes respectively rather than percentages.\n *\n * A key is always present, with an empty array when nothing was reported -- an\n * absent key would make every consumer handle undefined. A gap in a series means\n * no traffic in that bucket, not zero latency.\n */\nexport interface AppMetrics {\n start: string;\n end: string;\n /** Bucket width in seconds. Derived from the window, so not always 60. */\n period: number;\n series: Record<string, MetricPoint[]>;\n totals: {\n requests: number;\n client_errors: number;\n server_errors: number;\n gate_denied: number;\n /** Null when nothing was reported: no requests is not the same as 0ms. */\n latency_avg_ms: number | null;\n /** Estimated from a bounded sample of requests, not from all of them. */\n latency_p95_ms: number | null;\n cpu_avg: number | null;\n memory_avg_mb: number | null;\n };\n}\n\nexport type CreateAppResponse = ApiResponse<App>;\nexport type ListAppsResponse = ApiResponse<App[]>;\nexport type GetAppResponse = ApiResponse<App>;\nexport type PushResponse = ApiResponse<Snapshot>;\nexport type DeployResponse = ApiResponse<Deployment>;\nexport type PromoteResponse = ApiResponse<Deployment>;\nexport type RollbackResponse = ApiResponse<Deployment>;\nexport type ListSecretsResponse = ApiResponse<Secret[]>;\nexport type SetSecretResponse = ApiResponse<Secret>;\nexport type GetOrgResponse = ApiResponse<Organization>;\nexport type ListMembersResponse = ApiResponse<OrgMember[]>;\nexport type InviteMemberResponse = ApiResponse<OrgMember>;\nexport type GetLogsResponse = ApiResponse<LogPage>;\n", "export const DEFAULT_PORT = 3000;\n\nexport const SUPPORTED_RUNTIMES = [\n \"node-20\",\n \"node-22\",\n \"python-3.11\",\n \"python-3.12\",\n \"bun-1\",\n \"deno-2\",\n] as const;\n\nexport type SupportedRuntime = (typeof SUPPORTED_RUNTIMES)[number];\n\nexport const DEPLOYMENT_STATUSES = [\n \"building\",\n \"deploying\",\n \"running\",\n \"failed\",\n // A task that was live and then died on its own -- OOM, panic, ECS\n // replacement. The reconcile pass sets it, so anything enumerating statuses\n // has to know about it.\n \"crashed\",\n \"stopped\",\n] as const;\n\nexport const ENVIRONMENTS = [\"preview\", \"production\"] as const;\n\nexport const ROLES = [\"admin\", \"member\"] as const;\n\nexport const DEFAULT_API_URL = \"https://api.videan.xyz/v1\";\n\n/** The zone every app's hostname hangs off. */\nexport const APP_DOMAIN = process.env.CLOUD_APP_DOMAIN || \"videan.xyz\";\n\n/**\n * The public URL of an app, from its slug.\n *\n * One definition, because it was previously written out by hand in three places\n * and one of them omitted the scheme -- so a deployment's `url` came back as a\n * bare `slug.videan.xyz` that no terminal, browser or `fetch` would follow,\n * while the app resource reported the same app as `https://slug.videan.xyz`.\n * HTTP redirects to HTTPS anyway, so the scheme is not optional in practice.\n */\nexport function appUrl(slug: string): string {\n return `https://${slug}.${APP_DOMAIN}`;\n}\n\n/**\n * Prefix for a single deployment's own hostname.\n *\n * Every deployment is reachable before it is promoted, which is what lets an\n * agent verify its own work without production riding on the answer. The label is\n * a hash of the deployment id rather than the id itself: nanoid's alphabet\n * contains `_` and uppercase, neither of which is legal in a DNS label, and\n * lowercasing or substituting would map two distinct deployments onto one\n * hostname -- serving whichever the router looked up first.\n *\n * The prefix keeps the namespace from colliding with app slugs: `*.videan.xyz`\n * is one flat namespace, and an app legitimately slugged the same as some\n * deployment's hash would otherwise steal its traffic.\n */\nexport const PREVIEW_LABEL_PREFIX = \"d-\";\n\nexport function previewUrl(label: string): string {\n return `https://${PREVIEW_LABEL_PREFIX}${label}.${APP_DOMAIN}`;\n}\n\n/** True for a hostname label that belongs to the preview namespace. */\nexport function isPreviewLabel(label: string): boolean {\n return label.startsWith(PREVIEW_LABEL_PREFIX);\n}\n\nexport const CONFIG_FILE_NAME = \"cloud.yaml\";\n\nexport const IGNORE_FILE_NAME = \".cloudignore\";\n\nexport const MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; // 50MB\n\nexport const MAX_FILES_PER_PUSH = 10_000;\n\n/**\n * Public mailbox providers that can never be claimed as an SSO domain. A\n * domain claim routes every login for that domain to one org's IdP and lets\n * that IdP assert addresses under it \u2014 fine for a domain the org owns,\n * account capture for a mailbox anyone can register on. Not exhaustive (it\n * cannot be); the common ones an admin might try are what matters.\n */\nexport const PUBLIC_EMAIL_DOMAINS = [\n \"gmail.com\",\n \"googlemail.com\",\n \"outlook.com\",\n \"hotmail.com\",\n \"live.com\",\n \"msn.com\",\n \"yahoo.com\",\n \"ymail.com\",\n \"icloud.com\",\n \"me.com\",\n \"mac.com\",\n \"aol.com\",\n \"proton.me\",\n \"protonmail.com\",\n \"pm.me\",\n \"zoho.com\",\n \"gmx.com\",\n \"gmx.net\",\n \"mail.com\",\n \"fastmail.com\",\n \"hey.com\",\n \"tutanota.com\",\n \"yandex.com\",\n \"qq.com\",\n \"163.com\",\n \"126.com\",\n \"naver.com\",\n] as const;\n", "/**\n * Runtime detection and Dockerfile generation.\n *\n * This is the single source of truth for \"what kind of app is this and how do we\n * containerize it\". The control API generates build contexts from it and the\n * builder service reports runtime from it; two implementations would eventually\n * disagree about the same snapshot.\n */\n\nexport type Runtime = \"custom\" | \"nextjs\" | \"nodejs\" | \"python\" | \"go\";\n\n/**\n * Every app is served on this port.\n *\n * The edge router resolves a task to `ip:APP_PORT`, so detection cannot pick a\n * per-runtime port -- a Python app on 8000 would simply be unroutable. Generated\n * Dockerfiles set PORT=3000 and EXPOSE 3000; a project supplying its own\n * Dockerfile must listen here too.\n */\nexport const APP_PORT = 3000;\n\nexport const RUNTIMES: Runtime[] = [\"custom\", \"nextjs\", \"nodejs\", \"python\", \"go\"];\n\n/**\n * Narrows a string that crossed a boundary as `string` -- the builder reports the\n * detected runtime as plain text, and a caller keying a table off it needs to know\n * the value is one of ours rather than assume it.\n */\nexport function isRuntime(value: string): value is Runtime {\n return (RUNTIMES as string[]).includes(value);\n}\n\nexport interface DetectionResult {\n runtime: Runtime;\n port: number;\n /** True when the project shipped its own Dockerfile and we generate nothing. */\n hasDockerfile: boolean;\n /** Why this runtime was chosen -- surfaced in build logs. */\n reason: string;\n}\n\nconst NEXT_CONFIG = /^next\\.config\\.(js|cjs|mjs|ts|mts)$/;\n\n/**\n * The AWS Lambda Web Adapter, injected into every generated image.\n *\n * It is what lets one contract (\"serve HTTP on port 3000\") run on Lambda at all:\n * Lambda speaks an invoke API, not HTTP, and the adapter translates between them\n * in front of an ordinary web server. The app itself needs no Lambda awareness,\n * which is the whole point -- the same code and the same image serve both\n * execution modes.\n *\n * Injected **unconditionally**, including for apps that will only ever run on\n * Fargate. `/opt/extensions` binaries are started by the Lambda runtime and by\n * nothing else, so under Fargate this is an unused ~4 MB file: inert, not\n * conditional. That is deliberate and load-bearing -- it means switching an app\n * between `container` and `serverless` is a pointer move over the image that\n * already exists, not a rebuild. A mode-conditional Dockerfile would make the\n * image mode-specific and turn every mode change into a full build.\n *\n * Pinned to an exact adapter version for the same reason every base image here is:\n * `:latest` would silently change what a rebuild of an unchanged snapshot produces.\n *\n * `AWS_LWA_READINESS_CHECK_PATH` is deliberately unset -- the adapter's default\n * is a TCP connect to the port, which is exactly the platform's contract. A path\n * would require every app to serve some agreed health route, which is a second\n * contract.\n */\nconst LAMBDA_ADAPTER = `COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter\nENV AWS_LWA_PORT=${APP_PORT}`;\n\n/**\n * Amazon's RDS CA bundle, installed into every generated Node image.\n *\n * The injected `DATABASE_URL` carries `sslmode=require`, which under libpq means\n * \"encrypt, do not verify\" -- so psycopg and pgx connect happily. `pg` >= 8.16\n * deliberately diverges and treats `require` as an alias for `verify-full`, and\n * the RDS roots are in no distribution's trust store, so **every** Node app that\n * opened its managed database died on `unable to get local issuer certificate`\n * after a successful build and a successful promotion. Reported as a crash, on\n * the one code path a tenant cannot work around from their own repo.\n *\n * Fixed by supplying the CA rather than by weakening the mode: `sslmode=no-verify`\n * would work for `pg` and is not a libpq mode at all, so it would break the two\n * runtimes that currently work, and one URL is injected for all of them. With the\n * bundle present, verify-full genuinely verifies.\n *\n * `ADD` fails the build if the fetch fails, which is the right direction: a\n * silently absent CA is the bug being fixed here.\n *\n * The `chmod` is what makes it work under Lambda as well as Fargate. Docker gives\n * a file fetched by `ADD <url>` mode 600 owned by root, and the generated node\n * images declare no `USER`, so under Fargate the app runs as root and reads it\n * fine -- but Lambda executes a container image as its own unprivileged sandbox\n * user, which cannot open a root-only file. Node's response to an unreadable\n * `NODE_EXTRA_CA_CERTS` is to warn and carry on with the default store, so the\n * symptom is not a startup failure but the original bug returning verbatim:\n * `unable to get local issuer certificate`, from an image whose environment\n * plainly names the bundle. Written as a `RUN` rather than `ADD --chmod=644`\n * because that flag needs BuildKit and CodeBuild builds these with the classic\n * builder.\n */\nconst RDS_CA = `# Lets a managed-Postgres connection be verified, not merely encrypted. See\n# packages/shared/src/detect.ts -- without this, \\`pg\\` cannot connect at all.\nADD https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem /etc/ssl/certs/rds-ca.pem\nRUN chmod 644 /etc/ssl/certs/rds-ca.pem\nENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/rds-ca.pem`;\n\n/**\n * Detects the runtime from a snapshot's file list.\n *\n * Paths are relative and may carry a leading \"./\". Order matters: a project can\n * satisfy several checks at once (a Next.js app has a package.json too), so the\n * most specific signal has to win.\n */\nexport function detectRuntime(files: string[]): DetectionResult {\n const set = new Set(files.map((f) => f.replace(/^\\.\\//, \"\")));\n\n // A hand-written Dockerfile always wins -- it is an explicit instruction.\n if (set.has(\"Dockerfile\") || set.has(\"dockerfile\")) {\n return {\n runtime: \"custom\",\n port: APP_PORT,\n hasDockerfile: true,\n reason: \"project provided a Dockerfile\",\n };\n }\n\n const hasPackageJson = set.has(\"package.json\");\n const hasNextConfig = [...set].some((f) => NEXT_CONFIG.test(f));\n const hasRequirements = set.has(\"requirements.txt\");\n const hasPyproject = set.has(\"pyproject.toml\");\n const hasGoMod = set.has(\"go.mod\");\n\n if (hasPackageJson && hasNextConfig) {\n return {\n runtime: \"nextjs\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found package.json and a next.config file\",\n };\n }\n\n if (hasPackageJson) {\n return {\n runtime: \"nodejs\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found package.json\",\n };\n }\n\n if (hasRequirements || hasPyproject) {\n return {\n runtime: \"python\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: hasRequirements ? \"found requirements.txt\" : \"found pyproject.toml\",\n };\n }\n\n if (hasGoMod) {\n return {\n runtime: \"go\",\n port: APP_PORT,\n hasDockerfile: false,\n reason: \"found go.mod\",\n };\n }\n\n throw new Error(\n \"Could not detect a runtime. Include a package.json, requirements.txt, \" +\n \"pyproject.toml, go.mod, or your own Dockerfile.\",\n );\n}\n\n/** The Python entry point to run, in order of preference. */\nfunction pythonEntry(files: string[]): string | null {\n const set = new Set(files.map((f) => f.replace(/^\\.\\//, \"\")));\n for (const candidate of [\"main.py\", \"app.py\", \"server.py\", \"manage.py\"]) {\n if (set.has(candidate)) return candidate;\n }\n return null;\n}\n\n/**\n * Generates the Dockerfile for a detected runtime.\n *\n * Throws for `custom`: that project has its own Dockerfile and overwriting it\n * would silently discard the author's build.\n */\nexport function generateDockerfile(files: string[], detection?: DetectionResult): string {\n const detected = detection ?? detectRuntime(files);\n\n if (detected.runtime === \"custom\") {\n throw new Error(\"Project supplies its own Dockerfile; nothing to generate\");\n }\n\n switch (detected.runtime) {\n case \"nextjs\":\n return `FROM public.ecr.aws/docker/library/node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm install\nCOPY . .\nRUN npm run build\n${RDS_CA}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV HOSTNAME=0.0.0.0\nEXPOSE ${APP_PORT}\nCMD [\"npm\", \"start\"]\n`;\n\n case \"nodejs\":\n return `FROM public.ecr.aws/docker/library/node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\n# npm ci needs a lockfile; fall back to install when there is none.\nRUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi\nCOPY . .\n${RDS_CA}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV HOST=0.0.0.0\nEXPOSE ${APP_PORT}\nCMD [\"npm\", \"start\"]\n`;\n\n case \"python\": {\n const entry = pythonEntry(files);\n // No recognisable entry point: install deps and let the image fail loudly\n // at start rather than guessing a module name.\n const cmd = entry\n ? `CMD [\"python\", \"${entry}\"]`\n : `CMD [\"sh\", \"-c\", \"echo 'No entry point found (expected main.py, app.py, server.py or manage.py)' >&2; exit 1\"]`;\n const install = `RUN if [ -f requirements.txt ]; then pip install --no-cache-dir -r requirements.txt; else pip install --no-cache-dir .; fi`;\n return `FROM public.ecr.aws/docker/library/python:3.12-slim\nWORKDIR /app\nCOPY . .\n${install}\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nENV PYTHONUNBUFFERED=1\nEXPOSE ${APP_PORT}\n${cmd}\n`;\n }\n\n case \"go\":\n return `FROM public.ecr.aws/docker/library/golang:1.22-alpine AS builder\nWORKDIR /build\nCOPY go.* ./\nRUN go mod download\nCOPY . .\nRUN CGO_ENABLED=0 go build -o app .\n\nFROM public.ecr.aws/docker/library/alpine:3.20\nWORKDIR /app\nCOPY --from=builder /build/app .\n${LAMBDA_ADAPTER}\nENV PORT=${APP_PORT}\nEXPOSE ${APP_PORT}\nCMD [\"./app\"]\n`;\n }\n}\n\n/**\n * Derives an app's URL slug from its name.\n *\n * The slug becomes a DNS label (`<slug>.videan.xyz`), so it must be lowercase\n * alphanumeric with inner hyphens and at most 63 characters.\n */\nexport function slugify(name: string): string {\n return name\n .toLowerCase()\n .normalize(\"NFKD\")\n // Strip combining marks so \"Caf\u00E9\" becomes \"cafe\", not \"caf\".\n .replace(/[\u0300-\u036F]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 63)\n // Slicing can leave a trailing hyphen, which is not a valid DNS label.\n .replace(/-+$/g, \"\");\n}\n", "/**\n * Fargate task sizing.\n *\n * Every app used to run at 256 CPU / 512 MB, hardcoded in `ecs-app.ts`. That is\n * below the working set of a stock Next.js server, so those apps were killed by\n * the OOM killer -- which surfaces as a container that exits without a message,\n * indistinguishable from a crash in the app's own code. Sizing is therefore not\n * a tuning knob; at the old default some runtimes could not work at all.\n *\n * Fargate accepts only specific (cpu, memory) pairs and rejects everything else\n * at RegisterTaskDefinition time. Validating here rather than there means the\n * caller is told which values are legal instead of reading an AWS error, and the\n * CLI can reject a bad `--memory` before a build starts.\n */\n\nimport type { Runtime } from \"./detect.js\";\n\n/**\n * Legal memory values (MiB) for each CPU unit, from the Fargate task-size table.\n *\n * Expressed as a range plus a step rather than a literal list because the larger\n * tiers have hundreds of legal values; a list would be unreadable and would drift.\n */\nconst FARGATE_SIZES: Record<number, { min: number; max: number; step: number }> = {\n 256: { min: 512, max: 2048, step: 512 },\n 512: { min: 1024, max: 4096, step: 1024 },\n 1024: { min: 2048, max: 8192, step: 1024 },\n 2048: { min: 4096, max: 16384, step: 1024 },\n 4096: { min: 8192, max: 30720, step: 1024 },\n 8192: { min: 16384, max: 61440, step: 4096 },\n 16384: { min: 32768, max: 122880, step: 8192 },\n};\n\nexport const FARGATE_CPUS = Object.keys(FARGATE_SIZES)\n .map(Number)\n .sort((a, b) => a - b);\n\n/**\n * 256 CPU is a special case: its legal memory values are 512, 1024 and 2048 --\n * it skips 1536, so the uniform step above would admit a value Fargate rejects.\n */\nconst CPU_256_MEMORY = [512, 1024, 2048];\n\nexport interface SizeValidation {\n ok: boolean;\n /** Why the pair is illegal, naming the legal alternatives. */\n error?: string;\n /** The legal memory values for the given CPU, for building an error message. */\n allowed?: number[];\n}\n\nfunction legalMemory(cpu: number): number[] {\n if (cpu === 256) return CPU_256_MEMORY;\n const spec = FARGATE_SIZES[cpu];\n if (!spec) return [];\n const values: number[] = [];\n for (let m = spec.min; m <= spec.max; m += spec.step) values.push(m);\n return values;\n}\n\n/**\n * Checks a (cpu, memory) pair against the Fargate table.\n *\n * Deliberately strict rather than snapping to the nearest legal value: silently\n * giving an app 2 GB when its owner asked for 1.5 GB means the bill and the\n * behaviour do not match what was requested, and a size that is quietly changed\n * is a size nobody can reason about when the app OOMs anyway.\n */\nexport function validateSize(cpu: number, memory: number): SizeValidation {\n if (!FARGATE_SIZES[cpu]) {\n return {\n ok: false,\n error: `cpu must be one of ${FARGATE_CPUS.join(\", \")} (got ${cpu})`,\n };\n }\n const allowed = legalMemory(cpu);\n if (!allowed.includes(memory)) {\n // Long lists are unhelpful in an error message; show the shape instead.\n const shown =\n allowed.length <= 6\n ? allowed.join(\", \")\n : `${allowed[0]}\u2013${allowed[allowed.length - 1]} in steps of ${allowed[1] - allowed[0]}`;\n return {\n ok: false,\n error: `memory ${memory} is not valid for cpu ${cpu}; allowed: ${shown}`,\n allowed,\n };\n }\n return { ok: true };\n}\n\nexport interface TaskSize {\n cpu: number;\n memory: number;\n}\n\n/**\n * The size an app gets when nobody chose one.\n *\n * Picked per runtime because the failure is runtime-specific: `next start` holds\n * a compiled server plus its module graph and does not fit in 512 MB, while a\n * Go binary is comfortable in it. Defaulting everything to the larger size would\n * quadruple the bill for the runtimes that do not need it; defaulting everything\n * to the smaller one is the bug this replaces.\n */\nexport function defaultSizeFor(runtime: Runtime): TaskSize {\n switch (runtime) {\n case \"nextjs\":\n return { cpu: 512, memory: 1024 };\n case \"nodejs\":\n case \"python\":\n // Comfortable for a framework plus dependencies, and the smallest legal\n // pair above the old default.\n return { cpu: 256, memory: 1024 };\n case \"go\":\n return { cpu: 256, memory: 512 };\n case \"custom\":\n // Unknown contents, so assume it is doing something real.\n return { cpu: 512, memory: 1024 };\n }\n}\n\n/**\n * How many tasks a size change or a scale-out may run at once.\n *\n * A bound exists because `instances` is caller-supplied and reaches RunTask\n * directly: a typo of 100 would launch a hundred Fargate tasks and bill for them\n * before anyone noticed. RunTask itself caps a single call at 10.\n */\nexport const MAX_INSTANCES = 10;\n", "/**\n * What a plan allows and what usage costs.\n *\n * Kept here rather than in the control API because three consumers need the same\n * numbers and must not disagree about them: the API enforces the limits, the\n * usage collector prices the metrics it records, and the CLI prints a bill. A\n * rate duplicated in two of those places is a bill that does not match the\n * invoice.\n *\n * Two things this deliberately is *not*:\n *\n * - **A payment integration.** Nothing here charges anyone. It computes what an\n * org owes; collecting it needs a payment processor and a human to accept its\n * terms, the same way SES production access does.\n * - **The authority on AWS cost.** These are the platform's *prices*. The real\n * cost of a Fargate task is in Cost Explorer, and the two will not match --\n * that difference is the margin, and pretending otherwise would make every\n * price change look like a metering bug.\n */\n\n/**\n * Metrics recorded in `usage_records`.\n *\n * `vcpu_seconds` and `gb_seconds` are what Fargate actually charges for, so they\n * are what a compute line is priced from; `task_seconds` is recorded alongside\n * them because it is the number a human recognises (\"this ran for 20 hours\") and\n * cannot be recovered from the other two without knowing the task size.\n *\n * `storage_mb`, not bytes: `usage_records.quantity` is an `integer`, and bytes\n * overflows int32 at 2.1 GB -- silently, wrapping negative, which would read as a\n * credit.\n *\n * `lambda_gb_seconds` and `lambda_invocations` are the serverless compute line,\n * deliberately *not* folded into `gb_seconds`. They price differently, they come\n * from a different source (CloudWatch's own measurement rather than arithmetic\n * over a deployment row), and an app that switched modes mid-month has to produce\n * an invoice a person can audit -- two lines that each say where they came from,\n * not one line that silently mixes two meters.\n */\nexport type UsageMetric =\n | \"task_seconds\"\n | \"vcpu_seconds\"\n | \"gb_seconds\"\n | \"requests\"\n | \"build_seconds\"\n | \"storage_mb\"\n | \"emails_sent\"\n | \"db_days\"\n | \"lambda_gb_seconds\"\n | \"lambda_invocations\"\n | \"egress_mb\";\n\nexport const USAGE_METRICS: UsageMetric[] = [\n \"task_seconds\",\n \"vcpu_seconds\",\n \"gb_seconds\",\n \"requests\",\n \"build_seconds\",\n \"storage_mb\",\n \"emails_sent\",\n \"db_days\",\n \"lambda_gb_seconds\",\n \"lambda_invocations\",\n \"egress_mb\",\n];\n\n/**\n * Metrics that are a level rather than a total.\n *\n * A day's `storage_mb` row is the last sample taken that day, not a sum of\n * samples -- summing a level double-counts every byte for every time it was\n * observed. Billing a level means averaging the samples across the period, which\n * is why the two kinds cannot be aggregated by the same code path.\n */\nexport const GAUGE_METRICS: UsageMetric[] = [\"storage_mb\"];\n\nexport function isGauge(metric: string): boolean {\n return (GAUGE_METRICS as string[]).includes(metric);\n}\n\n/**\n * Price per unit, in cents. Fractional on purpose: a request costs a fifth of a\n * millionth of a cent and rounding each one to a cent would bill $10,000 for a\n * million requests.\n *\n * `task_seconds` is priced at zero because compute is already billed through\n * `vcpu_seconds` and `gb_seconds`. It is in the table rather than absent from it\n * so that a new metric added without a price is a visible zero here instead of an\n * `undefined` multiplied into `NaN` further down.\n */\nexport const RATES: Record<UsageMetric, number> = {\n task_seconds: 0,\n // Fargate on-demand in us-east-1 is ~$0.04048/vCPU-hour and ~$0.004445/GB-hour.\n vcpu_seconds: 0.002,\n gb_seconds: 0.00025,\n // $0.20 per million requests.\n requests: 0.00002,\n // ~$0.012 per build-minute.\n build_seconds: 0.02,\n // $0.05 per GB-month, charged per MB-month.\n storage_mb: 0.005,\n // $0.50 per thousand.\n emails_sent: 0.05,\n // ~$3/month for a managed schema, charged per day it exists.\n db_days: 10,\n // Lambda list price is $0.0000166667 per GB-second; this carries roughly the\n // same markup over list as the Fargate rates above, so the two compute lines\n // are comparable and a mode switch does not look like a price change.\n lambda_gb_seconds: 0.0033,\n // Lambda's own $0.20 per million invocations, passed through at list. The\n // platform's markup on a request is already collected once by the `requests`\n // line at the edge, and one visit is one event -- marking it up on both meters\n // would charge twice for the same request.\n lambda_invocations: 0.00002,\n // Metered but not yet billed. AWS charges ~$0.09/GB out of the ALB and the\n // platform currently absorbs it, which is fine at guestbook scale and a real\n // margin leak the day someone serves video. Recorded now so the exposure is\n // a number on a dashboard instead of a surprise in Cost Explorer -- and so\n // that starting to charge for it is a rate change with history behind it,\n // not a new meter nobody was warned about. (MB, not bytes: `quantity` is an\n // int32 and bytes wraps negative at 2.1 GB, which would read as a credit.)\n egress_mb: 0,\n};\n\n/** Human unit for each metric, for a bill a person has to read. */\nexport const METRIC_LABELS: Record<UsageMetric, string> = {\n task_seconds: \"Task-seconds served\",\n vcpu_seconds: \"vCPU-seconds\",\n gb_seconds: \"GB-seconds\",\n requests: \"Requests\",\n build_seconds: \"Build seconds\",\n storage_mb: \"Object storage (MB-month)\",\n emails_sent: \"Emails sent\",\n db_days: \"Managed Postgres (app-days)\",\n lambda_gb_seconds: \"Serverless GB-seconds\",\n lambda_invocations: \"Serverless invocations\",\n egress_mb: \"Bandwidth out (MB)\",\n};\n\nexport interface Plan {\n id: string;\n name: string;\n /** Monthly subscription, in cents, before usage. */\n baseCents: number;\n /**\n * Usage covered by the base fee, in cents. An org that stays under this pays\n * exactly `baseCents` -- so the free plan is a real free tier rather than a\n * $0.03 invoice for a weekend project.\n */\n includedUsageCents: number;\n /** Apps that are not soft-deleted. */\n maxApps: number;\n maxInstancesPerApp: number;\n /** Ceilings on Fargate task size, which is the one knob that scales the bill. */\n maxCpu: number;\n maxMemory: number;\n /** Object-storage quota, enforced on upload. */\n maxStorageMb: number;\n postgres: boolean;\n customDomains: boolean;\n}\n\n/**\n * The plans.\n *\n * The limits that exist are the ones that bound *spend*: app count, instance\n * count and task size. There is deliberately no limit on requests, builds or\n * deploys -- an agent-built app that gets popular should get a bill, not an\n * outage, and a platform whose selling point is \"push and it is live\" cannot\n * answer a deploy with \"not on your plan\".\n */\nexport const PLANS: Record<string, Plan> = {\n free: {\n id: \"free\",\n name: \"Free\",\n baseCents: 0,\n includedUsageCents: 300,\n maxApps: 3,\n maxInstancesPerApp: 1,\n // A *ceiling*, not a promise of free compute: a container task at even\n // 256/512 running all month is ~$16 of usage against the $3 allowance.\n // That is why apps created on this plan default to serverless mode, which\n // meters nothing while idle -- the free tier is \"free while it sleeps\",\n // and a container app on this plan is a deliberate, metered choice.\n maxCpu: 512,\n maxMemory: 1024,\n maxStorageMb: 1024,\n postgres: true,\n customDomains: false,\n },\n pro: {\n id: \"pro\",\n name: \"Pro\",\n baseCents: 2000,\n includedUsageCents: 2000,\n maxApps: 50,\n maxInstancesPerApp: 4,\n maxCpu: 4096,\n maxMemory: 8192,\n maxStorageMb: 100 * 1024,\n postgres: true,\n customDomains: true,\n },\n scale: {\n id: \"scale\",\n name: \"Scale\",\n baseCents: 20000,\n includedUsageCents: 20000,\n maxApps: 500,\n maxInstancesPerApp: 10,\n maxCpu: 16384,\n maxMemory: 122880,\n maxStorageMb: 1024 * 1024,\n postgres: true,\n customDomains: true,\n },\n};\n\n/**\n * The plan for a stored `orgs.plan` value.\n *\n * Falls back to free rather than throwing: the column is free text and an\n * unrecognised value must not take an org's whole control surface down with a\n * 500. Free is the safe direction -- its limits are the tightest, so a typo\n * cannot silently grant a larger allowance than anyone agreed to.\n */\nexport function planFor(plan: string | null | undefined): Plan {\n return PLANS[plan || \"free\"] ?? PLANS.free;\n}\n\nexport interface UsageLine {\n metric: UsageMetric | string;\n label: string;\n quantity: number;\n unitPriceCents: number;\n /** Rounded to whole cents only at the line, never per unit. */\n cents: number;\n}\n\nexport interface Invoice {\n plan: string;\n periodStart: string;\n periodEnd: string;\n lines: UsageLine[];\n /** Everything the usage lines add up to, before the included allowance. */\n usageCents: number;\n baseCents: number;\n includedUsageCents: number;\n /** Usage beyond the allowance. Zero, never negative: credit does not roll over. */\n overageCents: number;\n totalCents: number;\n}\n\n/**\n * Prices a period's usage.\n *\n * `quantities` is metric \u2192 quantity, already aggregated over the period (sums for\n * counters, averages for gauges -- see `GAUGE_METRICS`, and note that this\n * function cannot tell which it was given, so aggregating wrongly produces a\n * plausible bill rather than an error).\n */\nexport function priceUsage(\n quantities: Record<string, number>,\n plan: Plan,\n period: { start: Date; end: Date },\n): Invoice {\n const lines: UsageLine[] = [];\n for (const metric of USAGE_METRICS) {\n const quantity = quantities[metric] ?? 0;\n const rate = RATES[metric];\n // A zero-priced metric is still reported when there is something to report:\n // task-seconds is how an owner checks that the compute line is plausible.\n if (quantity === 0) continue;\n lines.push({\n metric,\n label: METRIC_LABELS[metric],\n quantity: Math.round(quantity * 100) / 100,\n unitPriceCents: rate,\n cents: Math.round(quantity * rate),\n });\n }\n // Metrics recorded by a newer collector than this pricing table knows about.\n // Listed at zero rather than dropped: an unpriced metric that vanishes from the\n // bill is indistinguishable from one that was never collected.\n for (const metric of Object.keys(quantities)) {\n if ((USAGE_METRICS as string[]).includes(metric)) continue;\n if (!quantities[metric]) continue;\n lines.push({\n metric,\n label: metric,\n quantity: quantities[metric],\n unitPriceCents: 0,\n cents: 0,\n });\n }\n\n const usageCents = lines.reduce((acc, l) => acc + l.cents, 0);\n const overageCents = Math.max(0, usageCents - plan.includedUsageCents);\n return {\n plan: plan.id,\n periodStart: period.start.toISOString(),\n periodEnd: period.end.toISOString(),\n lines,\n usageCents,\n baseCents: plan.baseCents,\n includedUsageCents: plan.includedUsageCents,\n overageCents,\n totalCents: plan.baseCents + overageCents,\n };\n}\n\n/** `1234` \u2192 `\"$12.34\"`. For output a person reads, not for arithmetic. */\nexport function formatCents(cents: number): string {\n const sign = cents < 0 ? \"-\" : \"\";\n const abs = Math.abs(Math.round(cents));\n return `${sign}$${Math.floor(abs / 100)}.${String(abs % 100).padStart(2, \"0\")}`;\n}\n\nexport interface LimitCheck {\n ok: boolean;\n /** Names the limit and the plan, so the error says what to do about it. */\n error?: string;\n code?: string;\n}\n\nexport function allowed(): LimitCheck {\n return { ok: true };\n}\n\nfunction denied(code: string, error: string): LimitCheck {\n return { ok: false, error, code };\n}\n\n/** Whether one more app is within the plan. `current` excludes deleted apps. */\nexport function checkAppCount(plan: Plan, current: number): LimitCheck {\n if (current < plan.maxApps) return allowed();\n return denied(\n \"PLAN_LIMIT_APPS\",\n `The ${plan.name} plan allows ${plan.maxApps} app${plan.maxApps === 1 ? \"\" : \"s\"} ` +\n `and this org has ${current}. Delete one or upgrade the plan.`,\n );\n}\n\n/**\n * Whether a requested task shape is within the plan.\n *\n * Checked as a triple rather than three separate limits because they multiply:\n * the thing being bounded is the bill, and 4 instances of a small task costs the\n * same as one large one.\n */\nexport function checkTaskShape(\n plan: Plan,\n shape: { cpu?: number | null; memory?: number | null; instances?: number | null },\n): LimitCheck {\n if (shape.instances != null && shape.instances > plan.maxInstancesPerApp) {\n return denied(\n \"PLAN_LIMIT_INSTANCES\",\n `The ${plan.name} plan allows ${plan.maxInstancesPerApp} instance` +\n `${plan.maxInstancesPerApp === 1 ? \"\" : \"s\"} per app (asked for ${shape.instances}).`,\n );\n }\n if (shape.cpu != null && shape.cpu > plan.maxCpu) {\n return denied(\n \"PLAN_LIMIT_CPU\",\n `The ${plan.name} plan allows up to ${plan.maxCpu} CPU units per task (asked for ${shape.cpu}).`,\n );\n }\n if (shape.memory != null && shape.memory > plan.maxMemory) {\n return denied(\n \"PLAN_LIMIT_MEMORY\",\n `The ${plan.name} plan allows up to ${plan.maxMemory} MB per task (asked for ${shape.memory}).`,\n );\n }\n return allowed();\n}\n\n/** Whether a custom domain is within the plan. */\nexport function checkCustomDomain(plan: Plan): LimitCheck {\n if (plan.customDomains) return allowed();\n return denied(\n \"PLAN_LIMIT_DOMAINS\",\n `Custom domains are not included in the ${plan.name} plan. Every app keeps its ` +\n `platform hostname, which needs no configuration.`,\n );\n}\n", "/**\n * Who may reach a running app, as a ladder from narrowest to widest.\n *\n * This replaces `auth_required`, which could only distinguish \"gated\" from\n * \"open\" and so collapsed the two settings owners actually reach for -- \"anyone\n * with the link\" and \"only the people I invite\" -- into the same switch as\n * \"anyone in my org\".\n *\n * The tiers are deliberately ordered and each is a strict superset of the one\n * before it, so widening is always one step and there is never a pair of\n * settings whose relative reach is ambiguous.\n *\n * Note what this is *not*: it governs visiting the deployed app at its\n * hostname, not managing it through the API. Those are separate on purpose --\n * `lib/access.ts` answers the second one -- so an org member who cannot open a\n * `private` app in a browser may still hold write on it in the console. The\n * alternative, letting a visibility setting revoke an org member's ability to\n * deploy, would make an access control into an outage.\n */\nexport const VISIBILITIES = [\"private\", \"org\", \"authenticated\", \"public\"] as const;\n\nexport type Visibility = (typeof VISIBILITIES)[number];\n\n/**\n * The default for a new app, and the widest tier that still requires a session.\n *\n * Matches what `auth_required` defaulted to (true, meaning org-or-share), so\n * creating an app has not changed who can reach it.\n */\nexport const DEFAULT_VISIBILITY: Visibility = \"org\";\n\nexport function isVisibility(value: unknown): value is Visibility {\n return typeof value === \"string\" && (VISIBILITIES as readonly string[]).includes(value);\n}\n\n/**\n * Reads a stored value as a tier, treating anything unrecognized as the\n * **narrowest** one.\n *\n * The column is `text`, and this repo has twice shipped a value nothing reads\n * (`org_members.role = 'owner'`, `deployments.status = 'rolled_back'`) -- both\n * stored happily and were silently ignored. Here the same slip would decide\n * whether strangers can read a tenant's app, so an unknown value must fail\n * closed. `private` is still reachable by its org's admins, so the worst case of\n * failing closed is an app its owners can fix; the worst case of failing open is\n * a disclosure they cannot undo.\n */\nexport function visibilityOf(value: unknown): Visibility {\n return isVisibility(value) ? value : \"private\";\n}\n\n/**\n * Whether a tier admits a visitor with no session at all.\n *\n * This is what the legacy `auth_required` boolean encodes, which is why every\n * write to `visibility` also writes `auth_required = !isPublic(v)`: the deployed\n * edge router may still be reading only the boolean.\n */\nexport function isPublic(v: Visibility): boolean {\n return v === \"public\";\n}\n\n/**\n * Human-readable label, used by the CLI. The console deliberately imports no\n * workspace package, so its copy of these lives in\n * `apps/console-v2/lib/api.ts` (`VISIBILITY`) \u2014 keep the wording identical, or\n * the same tier reads differently in the two places a person sees it.\n */\nexport const VISIBILITY_LABELS: Record<Visibility, string> = {\n private: \"Only people with access\",\n org: \"Anyone in your organization\",\n authenticated: \"Anyone signed in to Videan\",\n public: \"Anyone with the link\",\n};\n", "import Conf from \"conf\";\n\ninterface CloudConfig {\n apiKey?: string;\n defaultOrg?: string;\n currentApp?: string;\n baseUrl?: string;\n}\n\nconst config = new Conf<CloudConfig>({\n projectName: \"cloud-cli\",\n schema: {\n apiKey: { type: \"string\" },\n defaultOrg: { type: \"string\" },\n currentApp: { type: \"string\" },\n baseUrl: { type: \"string\" },\n },\n});\n\n/**\n * An override from the environment, checked under both prefixes.\n *\n * The bin is `videan`; the packages are `@cloud/*`. Accepting both spellings is\n * cheaper than being wrong about which one someone reaches for.\n */\nfunction fromEnv(name: string): string | undefined {\n const value = process.env[`VIDEAN_${name}`] || process.env[`CLOUD_${name}`];\n return value ? value : undefined;\n}\n\n/**\n * The environment wins over the saved config.\n *\n * The platform's premise is that an agent does the deploying, and an agent runs\n * in a non-interactive shell with no config file and no way to answer a password\n * prompt. `VIDEAN_API_KEY` is how it authenticates without `login` at all.\n */\nexport function getApiKey(): string | undefined {\n return fromEnv(\"API_KEY\") || config.get(\"apiKey\");\n}\n\nexport function setApiKey(key: string): void {\n config.set(\"apiKey\", key);\n}\n\nexport function getDefaultOrg(): string | undefined {\n // Both spellings again: the README documents VIDEAN_ORG_ID (the value *is*\n // an id), and the first real user set exactly that while the code read only\n // VIDEAN_ORG \u2014 so the CLI silently fell back to a stale saved org and\n // answered \"Not a member of this organization\" for a key that was fine.\n return fromEnv(\"ORG_ID\") || fromEnv(\"ORG\") || config.get(\"defaultOrg\");\n}\n\nexport function setDefaultOrg(org: string): void {\n config.set(\"defaultOrg\", org);\n}\n\nexport function getCurrentApp(): string | undefined {\n return fromEnv(\"APP_ID\") || fromEnv(\"APP\") || config.get(\"currentApp\");\n}\n\nexport function setCurrentApp(appId: string): void {\n config.set(\"currentApp\", appId);\n}\n\nexport function getBaseUrl(): string | undefined {\n return fromEnv(\"API_URL\") || config.get(\"baseUrl\");\n}\n\nexport function setBaseUrl(url: string): void {\n config.set(\"baseUrl\", url);\n}\n\n/** Where the saved config lives, so a stale value can be found and inspected. */\nexport function configPath(): string {\n return config.path;\n}\n\nexport function clearConfig(): void {\n config.clear();\n}\n", "import inquirer from \"inquirer\";\nimport chalk from \"chalk\";\n\n/**\n * The one gate every interactive prompt goes through. Agents, CI jobs and\n * scripts run this CLI on closed or piped stdin, where legacy inquirer\n * renders the prompt, then dies uncaught (ERR_USE_AFTER_CLOSE from its\n * force-close handler) when the stream ends. So: refuse up front on a\n * non-TTY with a message naming the flag alternative, and treat a stream\n * that closes mid-prompt (a PTY torn down by a dying harness passes the\n * isTTY check) as the same refusal rather than a crash.\n */\nexport async function prompt<T = Record<string, unknown>>(\n questions: Parameters<typeof inquirer.prompt>[0],\n nonInteractiveHint: string,\n): Promise<T> {\n if (!process.stdin.isTTY) {\n console.error(\n chalk.red(`This command needs a terminal to prompt. ${nonInteractiveHint}`),\n );\n process.exit(1);\n }\n try {\n return (await inquirer.prompt(questions)) as T;\n } catch (err) {\n if (isStreamClosed(err)) {\n console.error(chalk.red(`Input ended before the prompt was answered. ${nonInteractiveHint}`));\n process.exit(1);\n }\n throw err;\n }\n}\n\nfunction isStreamClosed(err: unknown): boolean {\n const e = err as { code?: string; message?: string } | undefined;\n return e?.code === \"ERR_USE_AFTER_CLOSE\" || /closed/i.test(e?.message ?? \"\");\n}\n\n/**\n * Inquirer's force-close handler runs from a readline \"close\" event, outside\n * any promise chain, so when a PTY dies mid-prompt the ERR_USE_AFTER_CLOSE it\n * throws is uncatchable at the call site \u2014 only here. Everything else still\n * crashes loudly.\n */\nexport function installPromptCrashGuard(): void {\n process.on(\"uncaughtException\", (err) => {\n if (isStreamClosed(err)) {\n console.error(\n chalk.red(\n \"Input stream closed mid-prompt. Re-run with the answers as flags \u2014 every prompt has one (see --help).\",\n ),\n );\n process.exit(1);\n }\n throw err;\n });\n}\n", "import http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport { spawn } from \"node:child_process\";\n\n/**\n * The loopback half of `videan login --browser`.\n *\n * The CLI listens on an ephemeral 127.0.0.1 port and opens the console's\n * /cli-auth page with that port and a random state. The page has the user\n * sign in (password, Google or SSO \u2014 whatever their account uses) and click\n * Authorize, which top-level-navigates back to\n * `http://127.0.0.1:<port>/callback?state=\u2026&token=\u2026`. The state must match or\n * the delivery is refused: without it, any page could push a token of the\n * attacker's choosing into the waiting CLI (login CSRF).\n *\n * The token never leaves the machine \u2014 the console page navigates to the\n * loopback interface, and the fragment-vs-query concern from the Google flow\n * does not apply (a fragment never reaches an HTTP server, which here is the\n * whole point of the query string).\n */\n\nexport interface CallbackServer {\n port: number;\n /** Resolves with the token, or rejects on deny/timeout/mismatch. */\n waitForToken: Promise<string>;\n close: () => void;\n}\n\nconst LANDING = (title: string, sub: string) => `<!doctype html>\n<meta charset=\"utf-8\"><title>${title}</title>\n<body style=\"font-family:system-ui;background:#101014;color:#e8e8ea;display:grid;place-items:center;min-height:90vh\">\n<div style=\"text-align:center\"><h1 style=\"font-weight:600\">${title}</h1><p style=\"color:#9a9aa2\">${sub}</p></div>`;\n\nexport function createCallbackServer(\n state: string,\n timeoutMs = 5 * 60 * 1000,\n redirectOrigin?: string,\n): Promise<CallbackServer> {\n return new Promise((resolveServer, rejectServer) => {\n let settle: { resolve: (t: string) => void; reject: (e: Error) => void };\n const waitForToken = new Promise<string>((resolve, reject) => {\n settle = { resolve, reject };\n });\n // The browser can deliver (or deny) before the caller has awaited; without\n // a standing handler that instant is an \"unhandled rejection\" warning. This\n // side chain absorbs it \u2014 the caller's own await still sees the rejection.\n waitForToken.catch(() => {});\n\n const server = http.createServer((req, res) => {\n const url = new URL(req.url || \"/\", \"http://127.0.0.1\");\n if (url.pathname !== \"/callback\") {\n res.writeHead(404).end();\n return;\n }\n // Wrong state: answer the request but keep waiting \u2014 refusing to settle\n // means a spray of forged deliveries cannot end the login early.\n if (url.searchParams.get(\"state\") !== state) {\n res.writeHead(403, { \"Content-Type\": \"text/html\" });\n res.end(LANDING(\"Request refused\", \"This authorization was not started by this CLI.\"));\n return;\n }\n // Ending on videan.xyz beats ending on a raw 127.0.0.1 URL, so when the\n // caller knows the console origin the outcome pages redirect back to it.\n // Only the exact-state branches redirect \u2014 a forged delivery gets local\n // HTML, never a bounce of the attacker's choosing.\n const land = (done: string, status: number, title: string, sub: string) => {\n if (redirectOrigin) {\n res.writeHead(302, { Location: `${redirectOrigin}/cli-auth?done=${done}` });\n res.end();\n } else {\n res.writeHead(status, { \"Content-Type\": \"text/html\" });\n res.end(LANDING(title, sub));\n }\n };\n if (url.searchParams.get(\"error\")) {\n land(\"denied\", 200, \"Login cancelled\", \"You can close this tab.\");\n finish(() => settle.reject(new Error(\"Authorization was declined in the browser.\")));\n return;\n }\n const token = url.searchParams.get(\"token\");\n if (!token) {\n res.writeHead(400, { \"Content-Type\": \"text/html\" });\n res.end(LANDING(\"Something went wrong\", \"No token arrived. Run `videan login` again.\"));\n finish(() => settle.reject(new Error(\"The browser sent no token.\")));\n return;\n }\n land(\"ok\", 200, \"You're logged in\", \"Return to your terminal \u2014 you can close this tab.\");\n finish(() => settle.resolve(token));\n });\n\n const timer = setTimeout(() => {\n finish(() => settle.reject(new Error(\"Timed out waiting for the browser (5 minutes).\")));\n }, timeoutMs);\n // A pending timer or server must not hold the process open after main ends.\n timer.unref();\n\n let done = false;\n function finish(outcome: () => void) {\n if (done) return;\n done = true;\n clearTimeout(timer);\n outcome();\n // Give the final response a beat to flush before tearing down.\n setTimeout(() => server.close(), 100).unref();\n }\n\n server.on(\"error\", (e) => rejectServer(e));\n server.listen(0, \"127.0.0.1\", () => {\n const address = server.address();\n if (!address || typeof address === \"string\") {\n rejectServer(new Error(\"Could not bind a loopback port.\"));\n return;\n }\n resolveServer({\n port: address.port,\n waitForToken,\n close: () => finish(() => settle.reject(new Error(\"closed\"))),\n });\n });\n });\n}\n\nexport function randomState(): string {\n return crypto.randomBytes(16).toString(\"hex\");\n}\n\n/**\n * The console origin that pairs with an API base URL: `api.<domain>/v1` \u2192\n * `https://<domain>`. Anything not shaped that way has no console we can\n * guess, and the caller falls back to the password prompt with an explanation.\n */\nexport function consoleOriginFor(baseUrl: string): string | null {\n try {\n const url = new URL(baseUrl);\n if (url.protocol !== \"https:\" || !url.hostname.startsWith(\"api.\")) return null;\n return `https://${url.hostname.slice(4)}`;\n } catch {\n return null;\n }\n}\n\n/** Best-effort `open <url>`; the URL is always printed too, so failure is fine. */\nexport function openBrowser(url: string): void {\n const [cmd, args] =\n process.platform === \"darwin\"\n ? [\"open\", [url]]\n : process.platform === \"win32\"\n ? [\"cmd\", [\"/c\", \"start\", \"\", url]]\n : [\"xdg-open\", [url]];\n try {\n spawn(cmd, args as string[], { stdio: \"ignore\", detached: true }).unref();\n } catch {\n // Printed URL is the fallback.\n }\n}\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { clearConfig, configPath } from \"../config.js\";\n\nexport const logoutCommand = new Command(\"logout\")\n .description(\"Forget the saved key, org, app and API URL\")\n .action(() => {\n // Clears everything, not just the key. The saved `baseUrl` is the value most\n // likely to be wrong and was, until now, only reachable by editing the\n // config file: a leftover URL from an earlier environment made every command\n // fail against a host that no longer served the API.\n const where = configPath();\n clearConfig();\n console.log(chalk.green(\"Logged out.\"));\n console.log(chalk.gray(` Cleared ${where}`));\n });\n", "import { Command } from \"commander\";\nimport inquirer from \"inquirer\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { SUPPORTED_RUNTIMES, CONFIG_FILE_NAME } from \"@cloud/shared\";\nimport { writeVideanMd } from \"../videan-md.js\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Initialize a new Cloud app in the current directory\")\n .option(\"--name <name>\", \"App name (defaults to the directory name)\")\n .option(\"--runtime <runtime>\", \"One of: \" + (SUPPORTED_RUNTIMES as unknown as string[]).join(\", \"))\n .option(\"--entrypoint <file>\", \"Entry file\", \"index.ts\")\n .option(\"--yes\", \"Take the defaults without prompting\")\n .action(async (opts) => {\n const configPath = path.join(process.cwd(), CONFIG_FILE_NAME);\n // Agents run in non-interactive shells; a prompt there is a hang. Flags or\n // defaults cover everything, and the config file is optional anyway \u2014 the\n // platform detects runtime and entrypoint from the pushed files.\n const interactive = process.stdin.isTTY && !opts.yes;\n\n if (await fs.pathExists(configPath)) {\n if (!interactive) {\n console.log(chalk.yellow(`${CONFIG_FILE_NAME} already exists; leaving it alone.`));\n } else {\n console.log(chalk.yellow(`${CONFIG_FILE_NAME} already exists in this directory.`));\n const { overwrite } = await inquirer.prompt([\n { type: \"confirm\", name: \"overwrite\", message: \"Overwrite?\", default: false },\n ]);\n if (!overwrite) {\n // Still drop the agent contract \u2014 that half of init is idempotent and\n // the reason a repeat run is worth anything.\n for (const f of await writeVideanMd(process.cwd())) console.log(chalk.green(`Wrote ${f}`));\n return;\n }\n }\n }\n\n if (!(await fs.pathExists(configPath)) || interactive) {\n const answers = interactive\n ? await inquirer.prompt([\n { type: \"input\", name: \"name\", message: \"App name:\", default: opts.name || path.basename(process.cwd()) },\n {\n type: \"list\",\n name: \"runtime\",\n message: \"Runtime:\",\n choices: SUPPORTED_RUNTIMES as unknown as string[],\n },\n { type: \"input\", name: \"entrypoint\", message: \"Entrypoint:\", default: opts.entrypoint },\n ])\n : {\n name: opts.name || path.basename(process.cwd()),\n runtime: opts.runtime || (SUPPORTED_RUNTIMES as unknown as string[])[0],\n entrypoint: opts.entrypoint,\n };\n\n const config = [\n `name: ${answers.name}`,\n `runtime: ${answers.runtime}`,\n `entrypoint: ${answers.entrypoint}`,\n \"\",\n ].join(\"\\n\");\n\n await fs.writeFile(configPath, config, \"utf-8\");\n console.log(chalk.green(`Created ${CONFIG_FILE_NAME}`));\n }\n\n // The part an agent actually needs: the platform contract, in the repo,\n // where the next model session will read it unprompted.\n for (const f of await writeVideanMd(process.cwd())) console.log(chalk.green(`Wrote ${f}`));\n });\n", "import fs from \"fs-extra\";\nimport path from \"node:path\";\n\n/**\n * Drops the platform contract into the project directory, where an agent's\n * eyes actually are.\n *\n * A README on npm is where instructions go to be technically published; the\n * repo an agent is editing is the one context it reliably reads. `videan init`\n * writes VIDEAN.md and appends a pointer to CLAUDE.md / AGENTS.md when those\n * already exist (never creating them \u2014 that would be claiming a file the\n * project's own conventions own). The single most important line in it is the\n * auth rule: without it, agents generate a login page for gated apps and every\n * visitor signs in twice.\n */\n\nexport const VIDEAN_MD = `# This project deploys to Videan\n\nDeploy: \\`videan deploy\\` (from this directory). Logs: \\`videan logs -f\\`.\nUndo a bad deploy: \\`videan rollback\\`. Full agent guide: https://videan.xyz/llms.txt\n\nThe contract: serve HTTP on **port 3000**. Runtime (Node/Python/Next.js/Go/\nDockerfile) is detected from the files \u2014 no config needed. If the app has a\nmanaged database, read \\`DATABASE_URL\\` from the environment. Log to stdout.\n\n## Auth \u2014 read this before writing any login code\n\nThis platform puts a sign-in gate in front of apps unless they are public.\n\n- **Gated app (visibility private/org/authenticated \u2014 the default): DO NOT\n build a login page.** Every request arrives with the visitor already\n authenticated: read \\`X-User-Id\\`, \\`X-User-Email\\`, \\`X-User-Roles\\`. If the\n app needs its own user records, find-or-create a local user from\n \\`X-User-Email\\` and start your session from that \u2014 never render a login\n form when the header is present.\n- **Public app** (\\`videan apps set --public\\`): the gate is out of the path,\n no headers arrive, bring your own auth if the app needs any. Required for\n anything that must receive webhooks from external services.\n\nSecrets: \\`videan secrets set KEY=value\\` (UPPERCASE keys) \u2014 injected as env\nvars on the next deploy. Never commit credentials to this repo instead.\n\n## Execution modes\n\nApps run as a **container** (default: always on, no cold starts) or\n**serverless** (scales to zero, cold start after idle \u2014 good for internal\ntools with sporadic traffic). Set at create (\\`videan apps create --mode\nserverless\\`) or later (\\`videan apps set --mode \u2026\\`, applied by the next\ndeploy). Serverless refuses cron, background jobs, WebSockets and custom\nDockerfiles; container mode supports all of them.\n`;\n\nconst POINTER =\n \"\\n## Videan\\n\\nThis project deploys to Videan \u2014 see VIDEAN.md for the platform contract\" +\n \" (port 3000, auth headers, deploy commands) before writing server or login code.\\n\";\n\nexport async function writeVideanMd(dir: string): Promise<string[]> {\n const wrote: string[] = [];\n const target = path.join(dir, \"VIDEAN.md\");\n if (!(await fs.pathExists(target))) {\n await fs.writeFile(target, VIDEAN_MD, \"utf-8\");\n wrote.push(\"VIDEAN.md\");\n }\n for (const name of [\"CLAUDE.md\", \"AGENTS.md\"]) {\n const p = path.join(dir, name);\n if (!(await fs.pathExists(p))) continue;\n const body = await fs.readFile(p, \"utf-8\");\n if (body.includes(\"VIDEAN.md\")) continue;\n await fs.appendFile(p, POINTER, \"utf-8\");\n wrote.push(`${name} (pointer)`);\n }\n return wrote;\n}\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { buildErrorExcerpt, collectFiles, requireApiKey, formatStatus, waitUntilRouted } from \"../util.js\";\n\nexport const deployCommand = new Command(\"deploy\")\n .description(\"Deploy the current directory\")\n .option(\"-a, --app <id>\", \"App ID to deploy to\")\n .option(\"-m, --message <msg>\", \"Deployment message\")\n .option(\"--env <environment>\", \"preview or production (default: production)\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or run `videan init` first.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n\n // Collect files\n const spinner = ora(\"Collecting files...\").start();\n const { files, binaryFiles, skipped } = await collectFiles(process.cwd());\n const binaryCount = Object.keys(binaryFiles).length;\n const fileCount = Object.keys(files).length + binaryCount;\n if (!fileCount) {\n spinner.fail(chalk.red(`Nothing to deploy: no files under ${process.cwd()}.`));\n process.exit(1);\n }\n if (skipped.length) {\n // Named, not counted. A snapshot silently missing an image is a bug report\n // from the future; the operator can act on a list.\n spinner.warn(chalk.yellow(`Skipped ${skipped.length} file(s):`));\n for (const file of skipped) console.log(chalk.gray(` ${file.path} \u2014 ${file.reason}`));\n spinner.start();\n }\n spinner.text = `Pushing ${fileCount} files${binaryCount ? ` (${binaryCount} binary)` : \"\"}...`;\n\n // Push\n const snapshot = await client.apps.push(appId, files, opts.message, binaryFiles);\n spinner.text = \"Triggering deployment...\";\n\n // Deploy\n if (opts.env && opts.env !== \"preview\" && opts.env !== \"production\") {\n spinner.fail(chalk.red(`--env must be preview or production, not ${opts.env}`));\n process.exit(1);\n }\n const deployment = await client.apps.deploy(appId, snapshot.id, opts.env);\n spinner.text = `Deploying (${deployment.status})...`;\n\n // Poll for status. A build takes minutes, so a single failed request must\n // not end the watch -- it used to `break`, which reported the deployment as\n // stuck in `building` when in fact nothing was wrong with it but one HTTP\n // call. Give up only after several consecutive failures, and say so.\n let current = deployment;\n let consecutiveErrors = 0;\n let lastError = \"\";\n while (current.status === \"building\" || current.status === \"deploying\") {\n await new Promise((r) => setTimeout(r, 5000));\n const elapsed = Math.round((Date.now() - Date.parse(current.createdAt)) / 1000);\n spinner.text = `${current.status === \"building\" ? \"Building\" : \"Starting\"}... (${elapsed}s)`;\n try {\n current = await client.apps.checkDeployment(deployment.id);\n consecutiveErrors = 0;\n } catch (e: any) {\n lastError = e?.message || String(e);\n if (++consecutiveErrors >= 5) {\n spinner.fail(chalk.red(`Lost track of the deployment: ${lastError}`));\n console.log(chalk.gray(` It may still finish. Check with \\`videan status\\`.`));\n process.exit(1);\n }\n }\n }\n\n if (current.status === \"running\") {\n // \"running\" is the API's view. Each edge router re-reads the route table\n // every ~10s, so an app's *first* deploy used to print success while its\n // URL still answered the router's own 404 -- the first thing anyone did\n // with the URL failed. Wait (bounded) until the edge knows the hostname.\n if (current.url) {\n spinner.text = \"Waiting for the edge to route the URL...\";\n await waitUntilRouted(/^https?:\\/\\//.test(current.url) ? current.url : `https://${current.url}`);\n }\n spinner.succeed(chalk.green(`Deployed successfully!`));\n // One line of doctrine at the exact moment an agent is reading output:\n // gated apps must not grow a login page, public apps must bring their own.\n try {\n const app = await client.apps.get(appId);\n const gated = app.visibility ? app.visibility !== \"public\" : app.authRequired !== false;\n console.log(\n gated\n ? chalk.gray(\n ` Access: ${app.visibility || \"org\"} \u2014 visitors sign in once at the edge; ` +\n `your app receives X-User-Id / X-User-Email on every request. Don't build a login page.`,\n )\n : chalk.gray(` Access: public \u2014 reachable by anyone; bring your own auth if it needs any.`),\n );\n } catch {\n /* cosmetic only \u2014 a failed lookup must not fail a finished deploy */\n }\n if (current.url) {\n // Deployments promoted before the API started storing a full URL have a\n // bare hostname here, which no terminal will linkify and no fetch will\n // follow.\n const url = /^https?:\\/\\//.test(current.url) ? current.url : `https://${current.url}`;\n console.log(chalk.cyan(` URL: ${url}`));\n }\n if (current.previewUrl) {\n // This version's own address. Live from the moment its tasks are up --\n // which is before promotion, so it is what an agent checks its own work\n // against -- and only for as long as they are: the poller retires the\n // version this deploy replaced, and a retired version's address stops\n // resolving with it.\n console.log(chalk.gray(` This version: ${current.previewUrl}`));\n }\n } else if (current.status === \"failed\" || current.status === \"crashed\") {\n // `crashed` means the container started and then died -- a failure the\n // caller has to see, and one this used to report as mere information with\n // a zero exit code, so a scripted deploy counted it as a success.\n // The API sends `failureReason` and a structured `failureDetail`; this used\n // to read an `error` field that does not exist, so every failure -- a bad\n // npm version, a crash on boot -- printed \"unknown error\".\n const detail = (current.failureDetail ?? null) as { cause?: string; message?: string; hint?: string } | null;\n const reason = detail?.message || current.failureReason || \"no reason was recorded\";\n spinner.fail(chalk.red(`Deployment ${current.status}: ${reason}`));\n if (detail?.hint) console.log(chalk.yellow(` Fix: ${detail.hint}`));\n\n const buildFailed =\n current.status === \"failed\" &&\n (detail?.cause === \"build_failed\" || /build/i.test(current.failureReason ?? \"\"));\n if (buildFailed) {\n // A build failure's cause is in the build log, not the app's runtime\n // logs (there is no runtime yet), so show the lines that matter here.\n try {\n const logs = await client.apps.buildLogs(deployment.id);\n const excerpt = buildErrorExcerpt(logs.lines.map((l) => l.message));\n if (excerpt.length) {\n console.log(chalk.gray(\" Build output:\"));\n for (const line of excerpt) console.log(chalk.gray(` ${line}`));\n }\n } catch {\n /* the pointer below still gets them to the full log */\n }\n console.log(chalk.gray(` Full build log: https://videan.xyz/apps/${appId}/deployments/${deployment.id}`));\n } else {\n console.log(chalk.gray(` Logs: videan logs --app ${appId}`));\n }\n process.exit(1);\n } else {\n spinner.info(`Deployment status: ${formatStatus(current.status)}`);\n process.exit(1);\n }\n });\n", "import fs from \"fs-extra\";\nimport path from \"node:path\";\nimport ignore from \"ignore\";\nimport chalk from \"chalk\";\n\n/** Larger than this and the push is refused rather than silently trimmed. */\nexport const MAX_FILE_BYTES = 10 * 1024 * 1024;\n\nexport interface CollectedFiles {\n /** Relative path \u2192 UTF-8 contents, ready for the push wire format. */\n files: Record<string, string>;\n /** Relative path \u2192 base64 contents, for files that are not valid UTF-8. */\n binaryFiles: Record<string, string>;\n /**\n * Relative paths left out, with the reason. Only oversized files land here\n * now that binaries have somewhere to go.\n */\n skipped: { path: string; reason: string }[];\n}\n\n/**\n * Collect all files from a directory, respecting .gitignore and .cloudignore.\n */\nexport async function collectFiles(dir: string): Promise<CollectedFiles> {\n const ig = ignore();\n\n // Always ignored, whether or not the project has a .gitignore -- and many\n // agent-generated projects do not.\n //\n // `.next`, `.turbo` and `coverage` are local build output. Uploading them is\n // not merely wasteful: the container runs `next build` over whatever it is\n // given, and a `.next` produced on a different platform and Node version is\n // exactly the kind of stale cache that makes a build fail or, worse, succeed\n // with the previous code in it.\n ig.add([\n \".git\",\n \"node_modules\",\n \".env\",\n \".env.*\",\n \".next\",\n \".turbo\",\n \"coverage\",\n \".DS_Store\",\n \"*.log\",\n ]);\n\n // Load .gitignore\n const gitignorePath = path.join(dir, \".gitignore\");\n if (await fs.pathExists(gitignorePath)) {\n const content = await fs.readFile(gitignorePath, \"utf-8\");\n ig.add(content);\n }\n\n // Load .cloudignore\n const cloudignorePath = path.join(dir, \".cloudignore\");\n if (await fs.pathExists(cloudignorePath)) {\n const content = await fs.readFile(cloudignorePath, \"utf-8\");\n ig.add(content);\n }\n\n const files: Record<string, string> = {};\n const binaryFiles: Record<string, string> = {};\n const skipped: { path: string; reason: string }[] = [];\n\n async function walk(currentDir: string): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n // Snapshot keys are slash-separated regardless of host OS: the API\n // refuses backslashes outright, and ignore() matches gitignore syntax,\n // which is slash-separated too \u2014 so on Windows a native relative path\n // both fails the push and silently escapes every .gitignore rule.\n const relativePath = path.relative(dir, fullPath).split(path.sep).join(\"/\");\n\n // ignore() matches directories only when the path is marked as one, and\n // pattern semantics differ between \"dir\" and \"dir/\" for anchored rules.\n const ignorePath = entry.isDirectory() ? `${relativePath}/` : relativePath;\n if (ig.ignores(ignorePath)) continue;\n\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.isFile()) {\n // Read bytes, not a string. `readFile(path, \"utf-8\")` replaces every\n // byte that is not valid UTF-8 with U+FFFD and reports success, so a\n // PNG read as text arrives corrupted with nothing to indicate it. The\n // round-trip is what decides which map a file belongs in: text goes in\n // `files` verbatim, everything else is base64 in `binaryFiles`. It is a\n // test of the bytes rather than of the extension, so an unknown\n // extension or a text file with one byte of Latin-1 in it is handled on\n // what it contains.\n const buffer = await fs.readFile(fullPath);\n if (buffer.byteLength > MAX_FILE_BYTES) {\n // Refused, not trimmed: the push is one JSON body, and a 200MB asset\n // would be an out-of-memory on the control API rather than a rejected\n // request. Named here so the fix is `.cloudignore` or object storage.\n skipped.push({\n path: relativePath,\n reason: `${Math.round(buffer.byteLength / 1024 / 1024)}MB, over the ${MAX_FILE_BYTES / 1024 / 1024}MB limit`,\n });\n continue;\n }\n const text = buffer.toString(\"utf-8\");\n if (Buffer.from(text, \"utf-8\").equals(buffer)) {\n files[relativePath] = text;\n } else {\n binaryFiles[relativePath] = buffer.toString(\"base64\");\n }\n }\n }\n }\n\n await walk(dir);\n return { files, binaryFiles, skipped };\n}\n\n/**\n * Format a deployment status with color.\n */\nexport function formatStatus(status: string): string {\n switch (status) {\n case \"running\":\n return chalk.green(status);\n case \"building\":\n case \"deploying\":\n return chalk.yellow(status);\n case \"failed\":\n case \"crashed\":\n return chalk.red(status);\n case \"stopped\":\n return chalk.gray(status);\n default:\n return status;\n }\n}\n\n/**\n * Format a table row for display.\n */\nexport function formatRow(columns: string[], widths: number[]): string {\n return columns.map((col, i) => col.padEnd(widths[i] || 20)).join(\" \");\n}\n\n/**\n * Require an API key or exit.\n */\nexport function requireApiKey(apiKey: string | undefined): asserts apiKey is string {\n if (!apiKey) {\n console.error(chalk.red(\"Not logged in. Run `videan login` first.\"));\n process.exit(1);\n }\n}\n\n/**\n * Require an organization or exit. Every app lives in an org, so commands that\n * list or create apps need one from --org or the saved default.\n */\nexport function requireOrg(orgId: string | undefined): string {\n if (!orgId) {\n console.error(chalk.red(\"No organization. Pass --org <id> or run `videan login` again.\"));\n process.exit(1);\n }\n return orgId;\n}\n\n// Re-exported rather than redefined: the CLI and the API have to agree on the\n// hostname, and they did not while each built it by hand.\nexport { appUrl } from \"@cloud/shared\";\n\n/**\n * `secrets set` takes `KEY value` or `KEY=value`. The second is the form the\n * docs and llms.txt teach; it used to be read as a key named \"KEY=value\" (a TTY\n * then prompted for a value, a non-TTY refused). Split at the first `=` only:\n * values -- base64, URLs with query strings -- are full of them.\n */\nexport function parseSecretArgs(key: string, value: string | undefined): { key: string; value: string | undefined } {\n if (value !== undefined || !key.includes(\"=\")) return { key, value };\n const eq = key.indexOf(\"=\");\n return { key: key.slice(0, eq), value: key.slice(eq + 1) };\n}\n\n/**\n * The lines of a failed build that say why it failed, for printing under the\n * failure. A CodeBuild log is hundreds of lines of phase chatter; the cause is\n * a handful of `npm error` / `ERROR:` lines in the middle of it. Docker's step\n * prefix (`#11 1.356 `) is stripped so the excerpt reads like the tool's output.\n */\nexport function buildErrorExcerpt(lines: string[], max = 8): string[] {\n const out: string[] = [];\n for (const raw of lines) {\n // Docker prints each failing line twice with different prefixes -- live as\n // `#11 1.356 npm error ...`, then in the summary as `0.905 npm error ...` --\n // so both the step number and the timestamp are optional.\n const line = raw.replace(/^(#\\d+\\s+)?(\\d+\\.\\d+\\s+)?/, \"\").trim();\n if (!line || line.startsWith(\"[Container]\")) continue;\n if (/npm notice|Changelog:|To update run:|complete log of this run/i.test(line)) continue;\n if (!/\\berror\\b|ERR!|not found|No matching|cannot find|failed to solve|exit code: [1-9]/i.test(line)) continue;\n if (!out.includes(line)) out.push(line);\n if (out.length >= max) break;\n }\n return out;\n}\n\n/** The body the edge router sends for a hostname missing from its route table. */\nexport const UNROUTED_MARKER = \"no deployment found for this app\";\n\n/**\n * Resolves once the edge router routes `url` -- anything but its own \"no\n * deployment found\" 404 counts, including a redirect to the login gate or the\n * app's own 404 -- or after `timeoutMs`, whichever is first. Never throws: this\n * only decides when to print success, and a network blip must not fail a\n * deploy that succeeded.\n */\nexport async function waitUntilRouted(\n url: string,\n { timeoutMs = 25_000, intervalMs = 1_000, fetchImpl = fetch }: { timeoutMs?: number; intervalMs?: number; fetchImpl?: typeof fetch } = {},\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n try {\n const res = await fetchImpl(url, { redirect: \"manual\", signal: AbortSignal.timeout(5_000) });\n if (res.status !== 404) return true;\n if (!(await res.text()).includes(UNROUTED_MARKER)) return true;\n } catch {\n /* not reachable yet, or a blip: keep waiting until the deadline */\n }\n await new Promise((r) => setTimeout(r, intervalMs));\n }\n return false;\n}\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { DEFAULT_API_URL } from \"@cloud/shared\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\n/**\n * Downloads a snapshot back out as a folder.\n *\n * The dashboard can read any version file by file; this is the bulk direction \u2014\n * the \"I lost my local copy\" recovery, and the honest completion of \"the deploy\n * history IS the version history\": a history you cannot restore from is a\n * museum. Defaults to the newest snapshot; any snapshot id restores any\n * version.\n *\n * Refuses a non-empty directory without --force: pull overwrites what it\n * lands on, and a half-mixed tree (their edits + an old snapshot) is worse\n * than either side whole.\n */\nexport const pullCommand = new Command(\"pull\")\n .description(\"Download a snapshot's files into a directory\")\n .argument(\"[snapshot]\", \"Snapshot id (defaults to the newest)\")\n .option(\"-a, --app <id>\", \"App ID to pull from\")\n .option(\"-o, --out <dir>\", \"Directory to write into\", \".\")\n .option(\"--force\", \"Write into a non-empty directory\")\n .action(async (snapshotArg, opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n // Same default the SDK applies: an agent with only VIDEAN_API_KEY set has\n // no saved config and no reason to name the production URL. DEFAULT_API_URL\n // carries a /v1 suffix (the SDK's paths omit it; this file's include it),\n // so strip it to one convention.\n const baseUrl = (getBaseUrl() || DEFAULT_API_URL).replace(/\\/v1\\/?$/, \"\");\n\n const get = async <T>(p: string): Promise<T> => {\n const res = await fetch(`${baseUrl}${p}`, {\n headers: { authorization: `Bearer ${apiKey}` },\n });\n if (!res.ok) {\n const body = (await res.json().catch(() => null)) as { error?: { message?: string } } | null;\n throw new Error(body?.error?.message || `${res.status} from ${p}`);\n }\n return ((await res.json()) as { data: T }).data;\n };\n\n const spinner = ora(\"Resolving snapshot...\").start();\n try {\n let snapshotId: string = snapshotArg ?? \"\";\n if (!snapshotId) {\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n spinner.fail(chalk.red(\"No app specified. Use --app, VIDEAN_APP_ID, or name a snapshot id.\"));\n process.exit(1);\n }\n const [latest] = await get<{ id: string; seq?: number | null }[]>(\n `/v1/apps/${appId}/snapshots?limit=1`,\n );\n if (!latest) {\n spinner.fail(chalk.red(\"This app has no snapshots yet.\"));\n process.exit(1);\n }\n snapshotId = latest.id;\n }\n\n const detail = await get<{ manifest: string[]; seq?: number | null }>(\n `/v1/snapshots/${snapshotId}`,\n );\n\n const outDir = path.resolve(opts.out);\n await fs.ensureDir(outDir);\n const existing = await fs.readdir(outDir);\n if (existing.length > 0 && !opts.force) {\n spinner.fail(chalk.red(`${outDir} is not empty. Pass --force to write into it anyway.`));\n process.exit(1);\n }\n\n spinner.text = `Pulling ${detail.manifest.length} files...`;\n let written = 0;\n for (const file of detail.manifest) {\n const body = await get<{ content: string; encoding: string }>(\n `/v1/snapshots/${snapshotId}/files/${file}`,\n );\n const target = path.join(outDir, file);\n // The manifest is server-listed S3 keys, but never write outside the\n // destination on any input.\n if (!target.startsWith(outDir + path.sep) && target !== outDir) continue;\n await fs.ensureDir(path.dirname(target));\n await fs.writeFile(\n target,\n body.encoding === \"base64\" ? Buffer.from(body.content, \"base64\") : body.content,\n );\n written++;\n spinner.text = `Pulling ${written}/${detail.manifest.length}...`;\n }\n\n spinner.succeed(\n chalk.green(\n `Pulled snapshot ${detail.seq ? `#${detail.seq}` : snapshotId} \u2014 ${written} files into ${outDir}`,\n ),\n );\n } catch (err) {\n spinner.fail(chalk.red(err instanceof Error ? err.message : String(err)));\n process.exit(1);\n }\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\nexport const logsCommand = new Command(\"logs\")\n .description(\"View app logs\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-f, --follow\", \"Follow log output\")\n .option(\"-n, --lines <n>\", \"Number of lines to show\", \"50\")\n .option(\"-s, --since <when>\", \"Start of the window: ISO timestamp or an offset like 15m, 2h, 7d\", \"1h\")\n .option(\"--until <when>\", \"End of the window (same formats as --since)\")\n .option(\"-q, --search <text>\", \"Only lines containing this text\")\n .option(\"--pattern <pattern>\", \"CloudWatch filter pattern, for the real syntax\")\n .option(\"--instance <prefix>\", \"Only one instance, by log stream prefix\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n\n if (opts.search && opts.pattern) {\n console.error(chalk.red(\"Use either --search or --pattern, not both.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const filters = {\n q: opts.search as string | undefined,\n pattern: opts.pattern as string | undefined,\n instance: opts.instance as string | undefined,\n };\n\n if (opts.follow) {\n // The generator holds the cursor, so a line is printed once. The old loop\n // re-fetched the same window every two seconds and reprinted all of it.\n for await (const entry of client.apps.followLogs(appId, { since: opts.since, ...filters })) {\n print(entry);\n }\n return;\n }\n\n const page = await client.apps.logs(appId, {\n limit: parseInt(opts.lines, 10),\n since: opts.since,\n until: opts.until,\n ...filters,\n });\n\n // Oldest first, so the newest line is the one left next to the prompt.\n for (const entry of page.events) print(entry);\n\n if (page.events.length === 0) {\n console.log(chalk.gray(`No log lines since ${page.start}.`));\n }\n if (page.truncated) {\n // Said out loud: otherwise a window with more lines than one scan can read\n // looks like a window that simply contained these.\n console.log(\n chalk.yellow(\n \"\\nThis is part of the window -- there are more lines than one request reads. Narrow --since or add --search.\",\n ),\n );\n }\n });\n\nfunction print(entry: { timestamp: string; level?: string; message: string }): void {\n console.log(`${chalk.gray(entry.timestamp)} ${formatLevel(entry.level)} ${entry.message}`);\n}\n\nfunction formatLevel(level?: string): string {\n switch (level) {\n case \"error\":\n return chalk.red(\"[ERR]\");\n case \"warn\":\n return chalk.yellow(\"[WRN]\");\n case \"info\":\n return chalk.blue(\"[INF]\");\n case \"debug\":\n return chalk.gray(\"[DBG]\");\n default:\n return chalk.gray(\"[LOG]\");\n }\n}\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\n/**\n * `videan metrics` -- the window summary, and optionally the buckets.\n *\n * Summary by default rather than the series: a terminal cannot plot 60 buckets\n * usefully, and the question being asked (\"is anything hitting my app, and is it\n * erroring\") is answered by the totals. `--series` prints the buckets for piping\n * somewhere that can graph them.\n */\nexport const metricsCommand = new Command(\"metrics\")\n .description(\"Traffic, latency and resource use for an app\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-s, --since <when>\", \"Start of the window: ISO timestamp or an offset like 15m, 2h, 7d\", \"1h\")\n .option(\"--until <when>\", \"End of the window (same formats as --since)\")\n .option(\"--series\", \"Print each bucket instead of just the summary\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const metrics = await client.apps.metrics(appId, { since: opts.since, until: opts.until });\n\n if (opts.json) {\n console.log(JSON.stringify(metrics, null, 2));\n return;\n }\n\n const t = metrics.totals;\n console.log(\n chalk.dim(`${metrics.start} \u2192 ${metrics.end} (${metrics.period}s buckets)`),\n );\n console.log();\n console.log(` ${label(\"Requests\")} ${t.requests}`);\n console.log(` ${label(\"4xx\")} ${count(t.clientErrors)}`);\n console.log(` ${label(\"5xx\")} ${count(t.serverErrors, true)}`);\n // Named for what it is rather than \"auth\": these are visitors the gate turned\n // away, which is the answer when an owner says their app looks idle.\n console.log(` ${label(\"Gate denied\")} ${count(t.gateDenied)}`);\n console.log(` ${label(\"Latency avg\")} ${ms(t.latencyAvgMs)}`);\n console.log(` ${label(\"Latency p95\")} ${ms(t.latencyP95Ms)} ${chalk.dim(\"(sampled)\")}`);\n console.log(` ${label(\"CPU avg\")} ${units(t.cpuAvg)}`);\n console.log(` ${label(\"Memory avg\")} ${mb(t.memoryAvgMb)}`);\n\n if (t.requests === 0) {\n // A gap is not published as a zero, so an app that has never been reached and\n // an app that is not deployed look the same here.\n console.log();\n console.log(chalk.dim(\"No requests recorded in this window.\"));\n }\n\n if (opts.series) {\n console.log();\n for (const [name, points] of Object.entries(metrics.series)) {\n if (points.length === 0) continue;\n console.log(chalk.bold(name));\n for (const p of points) console.log(` ${chalk.dim(p.t)} ${p.v}`);\n }\n }\n });\n\nfunction label(text: string): string {\n return chalk.dim(text.padEnd(13));\n}\n\nfunction count(n: number, severe = false): string {\n if (n === 0) return chalk.dim(\"0\");\n return severe ? chalk.red(String(n)) : chalk.yellow(String(n));\n}\n\n/** Null means nothing was reported, which is not the same as zero. */\nfunction ms(v: number | null): string {\n return v === null ? chalk.dim(\"\u2014\") : `${v} ms`;\n}\n\nfunction mb(v: number | null): string {\n return v === null ? chalk.dim(\"\u2014\") : `${v} MB`;\n}\n\nfunction units(v: number | null): string {\n return v === null ? chalk.dim(\"\u2014\") : `${v} CPU units`;\n}\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\nexport const rollbackCommand = new Command(\"rollback\")\n .description(\"Rollback to the previous production deployment\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const spinner = ora(\"Rolling back...\").start();\n\n try {\n const deployment = await client.apps.rollback(appId);\n spinner.succeed(chalk.green(\"Rollback successful!\"));\n if (deployment.url) {\n console.log(chalk.cyan(` URL: ${deployment.url}`));\n }\n } catch (error: any) {\n spinner.fail(chalk.red(`Rollback failed: ${error.message}`));\n process.exit(1);\n }\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { prompt } from \"../prompt.js\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey, formatRow, parseSecretArgs } from \"../util.js\";\n\nexport const secretsCommand = new Command(\"secrets\")\n .description(\"Manage app secrets\");\n\nsecretsCommand\n .command(\"set <key> [value]\")\n .description(\"Set a secret: `set KEY value` or `set KEY=value`\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (key: string, value: string | undefined, opts: any) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified.\"));\n process.exit(1);\n }\n\n // `KEY=value` is the form every doc and llms.txt teaches, so it has to work.\n ({ key, value } = parseSecretArgs(key, value));\n if (!key) {\n console.error(chalk.red(\"Missing secret name before '='. Usage: videan secrets set KEY=value\"));\n process.exit(1);\n }\n\n if (!value) {\n const answers = await prompt<{ value: string }>(\n [{ type: \"password\", name: \"value\", message: `Value for ${key}:` }],\n `Pass the value as an argument: videan secrets set ${key} <value>`,\n );\n value = answers.value;\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n await client.secrets.set(appId, key, value!);\n console.log(chalk.green(`Secret \"${key}\" set successfully.`));\n });\n\nsecretsCommand\n .command(\"list\")\n .description(\"List all secrets\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (opts: any) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const secrets = await client.secrets.list(appId);\n\n if (secrets.length === 0) {\n console.log(chalk.gray(\"No secrets configured.\"));\n return;\n }\n\n console.log(formatRow([\"KEY\", \"UPDATED\"], [30, 24]));\n for (const secret of secrets) {\n console.log(formatRow([secret.key, secret.updatedAt], [30, 24]));\n }\n });\n\nsecretsCommand\n .command(\"remove <key>\")\n .description(\"Remove a secret\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (key: string, opts: any) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n await client.secrets.delete(appId, key);\n console.log(chalk.green(`Secret \"${key}\" removed.`));\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { prompt } from \"../prompt.js\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp, getDefaultOrg, setCurrentApp } from \"../config.js\";\nimport { requireApiKey, requireOrg, formatRow, appUrl } from \"../util.js\";\nimport {\n DEFAULT_VISIBILITY,\n VISIBILITIES,\n VISIBILITY_LABELS,\n isVisibility,\n visibilityOf,\n APP_MODES,\n isAppMode,\n type App,\n type AppMode,\n type Visibility,\n} from \"@cloud/shared\";\n\nexport const appsCommand = new Command(\"apps\")\n .description(\"Manage apps\");\n\n/**\n * Validates `--access` here rather than letting the API answer 400.\n *\n * A tier is a security setting, so a typo has to fail loudly and name the legal\n * values \u2014 and it must fail *before* the rest of a `set` call is applied, since\n * a partially-applied settings write is worse than a refused one.\n */\nfunction accessOption(value: string | undefined): Visibility | undefined {\n if (value === undefined) return undefined;\n const tier = value.trim().toLowerCase();\n if (!isVisibility(tier)) {\n console.error(chalk.red(`Unknown --access value \"${value}\". Use one of: ${VISIBILITIES.join(\", \")}`));\n process.exit(1);\n }\n return tier;\n}\n\n/**\n * Validates `--mode` locally, for the same reason `--access` is validated here:\n * a mode that silently differs from the one asked for is the failure the\n * explicit field exists to prevent, and on `set` it must fail before any of the\n * other changes in the same call are applied.\n */\nfunction modeOption(value: string | undefined): AppMode | undefined {\n if (value === undefined) return undefined;\n const mode = value.trim().toLowerCase();\n if (!isAppMode(mode)) {\n console.error(chalk.red(`Unknown --mode value \"${value}\". Use one of: ${APP_MODES.join(\", \")}`));\n process.exit(1);\n }\n return mode;\n}\n\n/** Reads the tier from a response, falling back to the legacy boolean. */\nfunction describeAccess(app: Pick<App, \"authRequired\"> & { visibility?: string }): string {\n // An older API that does not send `visibility` still sends the boolean, and a\n // gated app is org-wide by default there.\n const tier = app.visibility ? visibilityOf(app.visibility) : app.authRequired ? \"org\" : \"public\";\n return `${tier} \u2014 ${VISIBILITY_LABELS[tier].toLowerCase()}`;\n}\n\nappsCommand\n .command(\"list\")\n .description(\"List all apps\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .action(async (opts: { org?: string }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const apps = await client.apps.list(orgId);\n\n if (apps.length === 0) {\n console.log(chalk.gray(\"No apps yet. Create one with `videan apps create`.\"));\n return;\n }\n\n const widths = [20, 24, 10, 44];\n console.log(formatRow([\"NAME\", \"ID\", \"STATUS\", \"URL\"], widths));\n for (const app of apps) {\n console.log(formatRow([app.name, app.id, app.status, appUrl(app.slug)], widths));\n }\n });\n\nappsCommand\n .command(\"create\")\n .description(\"Create a new app\")\n .option(\"-n, --name <name>\", \"App name\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .option(\"--postgres\", \"Provision a managed Postgres database\")\n // Both off unless asked for: storage outlives the code that wrote it and is\n // billed, and sending mail spends the platform's shared reputation.\n .option(\"--storage\", \"Give the app an object-storage prefix\")\n .option(\"--email\", \"Let the app send mail through the platform\")\n // The platform gates apps behind login by default; this opts out.\n .option(\"--public\", \"Serve the app without the platform login gate (same as --access public)\")\n .option(\"--access <tier>\", `Who may open the app: ${VISIBILITIES.join(\" | \")} (default: ${DEFAULT_VISIBILITY})`)\n .option(\n \"--stack <label>\",\n \"Group this app with its siblings (set the same label on a split frontend and backend)\",\n )\n .option(\n \"--mode <mode>\",\n `How the app runs: ${APP_MODES.join(\" | \")} (default: container). serverless scales to zero but runs no cron, jobs or WebSockets`,\n )\n .action(\n async (opts: {\n name?: string;\n org?: string;\n postgres?: boolean;\n storage?: boolean;\n email?: boolean;\n public?: boolean;\n access?: string;\n stack?: string;\n mode?: string;\n }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n let name = opts.name;\n if (!name) {\n const answers = await prompt<{ name: string }>(\n [{ type: \"input\", name: \"name\", message: \"App name:\" }],\n \"Pass the name: videan apps create --name <name>\",\n );\n name = answers.name;\n }\n if (!name) {\n console.error(chalk.red(\"An app name is required.\"));\n process.exit(1);\n }\n\n const visibility = accessOption(opts.access) ?? (opts.public ? \"public\" : undefined);\n const mode = modeOption(opts.mode);\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const app = await client.apps.create({\n name,\n orgId,\n postgres: opts.postgres ?? false,\n storage: opts.storage ?? false,\n email: opts.email ?? false,\n visibility,\n stack: opts.stack,\n mode,\n });\n setCurrentApp(app.id);\n\n console.log(chalk.green(`App \"${app.name}\" created (${app.id})`));\n console.log(chalk.gray(` Mode: ${app.mode ?? \"container\"}`));\n console.log(chalk.gray(` Access: ${describeAccess(app)}`));\n console.log(chalk.gray(` URL after first deploy: ${appUrl(app.slug)}`));\n // Runtime is detected from the pushed files, so there is nothing to pick here.\n console.log(chalk.gray(\" Push code with `videan deploy` \u2014 the runtime is auto-detected.\"));\n },\n );\n\nappsCommand\n .command(\"set\")\n .description(\"Change an app's settings (scale, rate limit, gate, capabilities)\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-n, --name <name>\", \"Rename the app (the URL keeps its original slug)\")\n .option(\"--cpu <units>\", \"CPU units per instance (256 = 0.25 vCPU)\")\n .option(\"--memory <mib>\", \"Memory per instance, in MiB\")\n .option(\"--instances <n>\", \"Number of instances to run\")\n .option(\"--rate-limit <rpm>\", \"Requests per minute per client\")\n .option(\"--no-rate-limit\", \"Remove the rate limit\")\n .option(\"--access <tier>\", `Who may open the app: ${VISIBILITIES.join(\" | \")}`)\n .option(\"--public\", \"Serve without the platform login gate (same as --access public)\")\n .option(\"--private\", \"Require platform login (leaves the tier alone if it is already gated)\")\n .option(\"--postgres\", \"Provision a managed Postgres database\")\n .option(\"--storage\", \"Give the app an object-storage prefix\")\n .option(\"--email\", \"Let the app send mail through the platform\")\n .option(\"--stack <label>\", \"Group this app with its siblings under a shared label\")\n .option(\"--no-stack\", \"Remove the app from its stack\")\n .option(\"--mode <mode>\", `How the app runs: ${APP_MODES.join(\" | \")} (applies on the next deploy or restart)`)\n .option(\"--json\", \"Print the raw response\")\n .action(\n async (opts: {\n app?: string;\n name?: string;\n cpu?: string;\n memory?: string;\n instances?: string;\n // commander sets this to false for --no-rate-limit and to the string for\n // --rate-limit, so the three states (untouched / removed / set) are\n // distinguishable: undefined, false, \"120\".\n rateLimit?: string | boolean;\n access?: string;\n public?: boolean;\n private?: boolean;\n postgres?: boolean;\n storage?: boolean;\n email?: boolean;\n // Same commander trick as rateLimit: --no-stack sets false, --stack sets\n // the string, untouched is undefined.\n stack?: string | boolean;\n mode?: string;\n json?: boolean;\n }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n\n if (opts.public && opts.private) {\n console.error(chalk.red(\"--public and --private contradict each other.\"));\n process.exit(1);\n }\n const access = accessOption(opts.access);\n if (access && (opts.public || opts.private)) {\n // Both would be sent and the API resolves `visibility` first, so the\n // boolean would be silently ignored. Refusing beats applying one of two\n // access settings the caller asked for.\n console.error(chalk.red(\"--access already says who may open the app; drop --public/--private.\"));\n process.exit(1);\n }\n\n const mode = modeOption(opts.mode);\n\n const changes: Parameters<CloudClient[\"apps\"][\"update\"]>[1] = {};\n if (opts.name) changes.name = opts.name;\n if (mode) changes.mode = mode;\n if (opts.cpu !== undefined) changes.cpu = intOption(\"--cpu\", opts.cpu);\n if (opts.memory !== undefined) changes.memory = intOption(\"--memory\", opts.memory);\n if (opts.instances !== undefined) {\n changes.instances = intOption(\"--instances\", opts.instances);\n }\n if (opts.rateLimit === false) changes.rateLimitPerMinute = null;\n else if (typeof opts.rateLimit === \"string\") {\n changes.rateLimitPerMinute = intOption(\"--rate-limit\", opts.rateLimit);\n }\n if (access) changes.visibility = access;\n if (opts.public) changes.authRequired = false;\n if (opts.private) changes.authRequired = true;\n if (opts.postgres) changes.postgres = true;\n if (opts.storage) changes.storage = true;\n if (opts.email) changes.email = true;\n if (opts.stack === false) changes.stack = null;\n else if (typeof opts.stack === \"string\") changes.stack = opts.stack;\n\n if (Object.keys(changes).length === 0) {\n // Otherwise this reports success having changed nothing, which reads as the\n // setting being applied.\n console.error(chalk.red(\"Nothing to change. Pass at least one option.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const app = await client.apps.update(appId, changes);\n\n if (opts.json) {\n console.log(JSON.stringify(app, null, 2));\n return;\n }\n\n console.log(chalk.green(`Updated ${app.name} (${app.id})`));\n console.log(` Mode: ${app.mode ?? \"container\"}`);\n console.log(` Access: ${describeAccess(app)}`);\n console.log(` Scale: ${app.cpu ?? \"default\"} CPU / ${app.memory ?? \"default\"} MiB`);\n console.log(\n ` Rate limit: ${app.rateLimitPerMinute ? `${app.rateLimitPerMinute}/min per client` : \"none\"}`,\n );\n if (app.restartRequired) {\n // Reported, not done: a restart is a brief interruption, and taking one as a\n // side effect of a settings write is not something to discover afterwards.\n console.log(\n chalk.yellow(\"\\n These settings apply to the next deploy. Run `videan deploy` to pick them up.\"),\n );\n }\n },\n );\n\n/** Parse a numeric flag, failing on junk rather than sending NaN to the API. */\nfunction intOption(flag: string, raw: string): number {\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n console.error(chalk.red(`${flag} expects a positive whole number, got \"${raw}\".`));\n process.exit(1);\n }\n return n;\n}\n\nappsCommand\n .command(\"delete <id>\")\n .description(\"Delete an app and all its resources\")\n .option(\"--yes\", \"Skip the confirmation prompt\")\n .action(async (id: string, opts: { yes?: boolean }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n if (!opts.yes) {\n const { confirm } = await prompt<{ confirm: boolean }>(\n [\n {\n type: \"confirm\",\n name: \"confirm\",\n message: `Delete app ${id}? This stops the app and destroys its database, secrets and snapshots.`,\n default: false,\n },\n ],\n \"Confirm with a flag: videan apps delete <id> --yes\",\n );\n if (!confirm) return;\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const { warnings } = await client.apps.delete(id);\n\n console.log(chalk.green(\"App deleted.\"));\n if (warnings.length > 0) {\n // Say so loudly: leaked AWS resources keep costing money.\n console.log(chalk.yellow(\"\\nSome resources could not be released:\"));\n for (const w of warnings) {\n console.log(chalk.yellow(` ${w.step}: ${w.error || \"unknown error\"}`));\n }\n }\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey, formatStatus, appUrl } from \"../util.js\";\nimport { VISIBILITY_LABELS, visibilityOf, type App } from \"@cloud/shared\";\n\n/**\n * Falls back to the legacy boolean, which is all an older API sends. A gated app\n * is org-wide there, so that is what the boolean means.\n */\nfunction accessLine(app: Pick<App, \"authRequired\"> & { visibility?: string }): string {\n const tier = app.visibility ? visibilityOf(app.visibility) : app.authRequired ? \"org\" : \"public\";\n return `${tier} \u2014 ${VISIBILITY_LABELS[tier].toLowerCase()}`;\n}\n\nexport const statusCommand = new Command(\"status\")\n .description(\"Show current app status\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const appId = opts.app || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const app = await client.apps.get(appId);\n // Health reads ECS rather than the deployment row, which keeps saying\n // \"running\" after the container dies.\n const health = await client.apps.health(appId);\n\n console.log(chalk.bold(app.name));\n console.log(` ID: ${app.id}`);\n console.log(` Created: ${app.createdAt}`);\n console.log(` Postgres: ${app.postgresEnabled ? \"enabled\" : \"disabled\"}`);\n console.log(` Access: ${accessLine(app)}`);\n console.log(` State: ${formatStatus(health.state)}`);\n console.log(` Healthy: ${health.healthy ? chalk.green(\"yes\") : chalk.red(\"no\")}`);\n console.log(` URL: ${chalk.cyan(health.url || appUrl(app.slug))}`);\n\n const task = health.task;\n if (task) {\n if (task.exit_code !== undefined && task.exit_code !== null) {\n const code = String(task.exit_code);\n console.log(` Exit code: ${task.exit_code === 0 ? code : chalk.red(code)}`);\n }\n if (task.stopped_reason) {\n console.log(` Stopped: ${chalk.yellow(task.stopped_reason)}`);\n }\n if (task.container_reason) {\n console.log(` Detail: ${chalk.yellow(task.container_reason)}`);\n }\n }\n\n if (!health.healthy) {\n console.log(chalk.gray(\"\\nRun `videan logs` to see why.\"));\n }\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { prompt } from \"../prompt.js\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getDefaultOrg } from \"../config.js\";\nimport { requireApiKey, requireOrg, formatRow } from \"../util.js\";\n\nexport const keysCommand = new Command(\"keys\")\n .description(\"Manage API keys for programmatic access\");\n\nkeysCommand\n .command(\"create <name>\")\n .description(\"Create an API key\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .option(\"-e, --expires <days>\", \"Expire the key after N days\")\n .option(\n \"--app <id>\",\n \"Pin the key to one app (a service token, for code running inside it)\",\n )\n .action(async (name: string, opts: { org?: string; expires?: string; app?: string }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n let expiresInDays: number | undefined;\n if (opts.expires !== undefined) {\n expiresInDays = Number(opts.expires);\n if (!Number.isInteger(expiresInDays) || expiresInDays < 1) {\n console.error(chalk.red(\"--expires must be a whole number of days, at least 1.\"));\n process.exit(1);\n }\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const key = await client.apiKeys.create({\n name,\n orgId,\n expiresInDays,\n appId: opts.app,\n });\n\n console.log(chalk.green(`Created key \"${key.name}\" (${key.id})`));\n // Printed once and never retrievable -- the server only stores a hash.\n console.log(`\\n ${chalk.bold(key.key)}\\n`);\n console.log(chalk.yellow(\"Copy it now. It cannot be shown again.\"));\n if (key.appId) {\n // Worth stating: this is the only kind of key that is safe to ship inside a\n // deployed app, and the difference is invisible in the key itself.\n console.log(\n chalk.gray(`Service token, scoped to app ${key.appId}. It cannot reach anything else.`),\n );\n } else {\n console.log(\n chalk.gray(\"Acts as you across every app in the org -- do not deploy it inside an app.\"),\n );\n }\n if (key.expiresAt) {\n console.log(chalk.gray(`Expires ${key.expiresAt}`));\n }\n });\n\nkeysCommand\n .command(\"list\")\n .description(\"List API keys\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .action(async (opts: { org?: string }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const keys = await client.apiKeys.list(orgId);\n\n if (keys.length === 0) {\n console.log(chalk.gray(\"No API keys. Create one with `videan keys create <name>`.\"));\n return;\n }\n\n const widths = [24, 24, 16, 22, 12, 24];\n console.log(formatRow([\"NAME\", \"ID\", \"PREFIX\", \"LAST USED\", \"STATE\", \"SCOPE\"], widths));\n for (const key of keys) {\n const state = key.revokedAt\n ? chalk.red(\"revoked\")\n : key.expiresAt && new Date(key.expiresAt) < new Date()\n ? chalk.yellow(\"expired\")\n : chalk.green(\"active\");\n console.log(formatRow(\n [\n key.name,\n key.id,\n `${key.keyPrefix}\u2026`,\n key.lastUsedAt || \"never\",\n state,\n key.appId ? `app ${key.appId}` : \"org-wide\",\n ],\n widths,\n ));\n }\n });\n\nkeysCommand\n .command(\"revoke <id>\")\n .description(\"Revoke an API key\")\n .option(\"--yes\", \"Skip the confirmation prompt\")\n .action(async (id: string, opts: { yes?: boolean }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n if (!opts.yes) {\n const { confirm } = await prompt<{ confirm: boolean }>(\n [{ type: \"confirm\", name: \"confirm\", message: `Revoke key ${id}?`, default: false }],\n \"Confirm with a flag: videan keys revoke <id> --yes\",\n );\n if (!confirm) return;\n }\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n await client.apiKeys.revoke(id);\n console.log(chalk.green(\"Key revoked.\"));\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getDefaultOrg, setDefaultOrg } from \"../config.js\";\nimport { requireApiKey, requireOrg, formatRow } from \"../util.js\";\n\nexport const orgsCommand = new Command(\"orgs\")\n .description(\"Manage organizations\");\n\norgsCommand\n .command(\"list\")\n .description(\"List your organizations\")\n .action(async () => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const orgs = await client.orgs.list();\n const current = getDefaultOrg();\n\n if (orgs.length === 0) {\n console.log(chalk.gray(\"No organizations. Create one with `videan orgs create <name>`.\"));\n return;\n }\n\n const widths = [24, 24, 10, 8];\n console.log(formatRow([\"NAME\", \"ID\", \"PLAN\", \"\"], widths));\n for (const org of orgs) {\n console.log(formatRow(\n [org.name, org.id, org.plan, org.id === current ? chalk.green(\"default\") : \"\"],\n widths,\n ));\n }\n });\n\norgsCommand\n .command(\"create <name>\")\n .description(\"Create an organization\")\n .action(async (name: string) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const org = await client.orgs.create(name);\n // First org becomes the default so subsequent commands need no --org.\n if (!getDefaultOrg()) setDefaultOrg(org.id);\n\n console.log(chalk.green(`Organization \"${org.name}\" created (${org.id})`));\n });\n\norgsCommand\n .command(\"use <id>\")\n .description(\"Set the default organization for this machine\")\n .action(async (id: string) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n\n // Verify membership before saving, so a typo fails here instead of on every\n // later command.\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const org = await client.orgs.get(id);\n setDefaultOrg(org.id);\n\n console.log(chalk.green(`Default organization set to \"${org.name}\".`));\n });\n\norgsCommand\n .command(\"members\")\n .description(\"List organization members\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .action(async (opts: { org?: string }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n const members = await client.orgs.members(orgId);\n\n const widths = [32, 24, 10];\n console.log(formatRow([\"EMAIL\", \"NAME\", \"ROLE\"], widths));\n for (const member of members) {\n console.log(formatRow(\n [member.email || member.user_id, member.name || \"-\", member.role],\n widths,\n ));\n }\n });\n\norgsCommand\n .command(\"invite <email>\")\n .description(\"Invite a member by email\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .action(async (email: string, opts: { org?: string }) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const orgId = requireOrg(opts.org || getDefaultOrg());\n\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n await client.orgs.invite(orgId, email);\n console.log(chalk.green(`Invited ${email}.`));\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey, formatRow } from \"../util.js\";\n\n/**\n * `videan storage` -- files an app keeps for itself, separate from its code.\n *\n * Upload and download go direct to S3 through a presigned URL, so a large file\n * does not travel through the control API. That is also why `put` reads and\n * writes real bytes rather than the text-only path `videan deploy` uses for\n * snapshots: this is where binaries belong.\n */\nexport const storageCommand = new Command(\"storage\").description(\"Store files for an app\");\n\nfunction client() {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n return new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n}\n\nfunction requireApp(opt?: string): string {\n const appId = opt || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n return appId;\n}\n\n/** 1.2 KB rather than 1234 -- the size is being read, not summed. */\nfunction human(bytes: number): string {\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n let n = bytes;\n let i = 0;\n while (n >= 1024 && i < units.length - 1) {\n n /= 1024;\n i++;\n }\n return `${i === 0 ? n : n.toFixed(1)} ${units[i]}`;\n}\n\nstorageCommand\n .command(\"ls [prefix]\")\n .description(\"List stored objects\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (prefix: string | undefined, opts: { app?: string; json?: boolean }) => {\n const appId = requireApp(opts.app);\n // Every page, not the first: a truncated listing that looks complete is how a\n // caller concludes a file was never uploaded.\n const objects = await client().storage.listAll(appId, prefix);\n\n if (opts.json) {\n console.log(JSON.stringify(objects, null, 2));\n return;\n }\n if (objects.length === 0) {\n console.log(chalk.gray(\"No objects stored. Upload one with `videan storage put <file>`.\"));\n return;\n }\n\n const widths = [52, 12, 24];\n console.log(formatRow([\"KEY\", \"SIZE\", \"MODIFIED\"], widths));\n for (const o of objects) {\n console.log(formatRow([o.key, human(o.size), o.lastModified || \"-\"], widths));\n }\n const total = objects.reduce((sum, o) => sum + o.size, 0);\n console.log(chalk.dim(`\\n${objects.length} object(s), ${human(total)}`));\n });\n\nstorageCommand\n .command(\"put <file> [key]\")\n .description(\"Upload a file (key defaults to the file's name)\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-t, --type <mime>\", \"Content type\")\n .action(async (file: string, key: string | undefined, opts: { app?: string; type?: string }) => {\n const appId = requireApp(opts.app);\n if (!(await fs.pathExists(file))) {\n console.error(chalk.red(`No such file: ${file}`));\n process.exit(1);\n }\n const body = await fs.readFile(file);\n const objectKey = key || path.basename(file);\n\n const stored = await client().storage.upload(appId, objectKey, body, {\n contentType: opts.type,\n });\n console.log(\n chalk.green(`Uploaded ${objectKey}`) + chalk.dim(` (${human(body.length)})`),\n );\n if (stored.etag) console.log(chalk.dim(`etag ${stored.etag}`));\n });\n\nstorageCommand\n .command(\"get <key> [file]\")\n .description(\"Download an object (defaults to the key's basename)\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (key: string, file: string | undefined, opts: { app?: string }) => {\n const appId = requireApp(opts.app);\n const bytes = await client().storage.download(appId, key);\n const dest = file || path.basename(key);\n await fs.writeFile(dest, bytes);\n console.log(chalk.green(`Wrote ${dest}`) + chalk.dim(` (${human(bytes.length)})`));\n });\n\nstorageCommand\n .command(\"rm <key>\")\n .description(\"Delete an object\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (key: string, opts: { app?: string }) => {\n const appId = requireApp(opts.app);\n await client().storage.delete(appId, key);\n // Deliberately not \"not found\" when it was already gone: delete is idempotent\n // server-side, and the state the caller wanted is the state they have.\n console.log(chalk.green(`Deleted ${key}`));\n });\n\nstorageCommand\n .command(\"url <key>\")\n .description(\"Print a temporary link to an object\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-e, --expires <seconds>\", \"Lifetime in seconds (30-3600)\", \"900\")\n .option(\"--upload\", \"Sign an upload (PUT) URL instead of a download\")\n .option(\"-d, --download <filename>\", \"Serve as a download with this filename\")\n .action(async (key: string, opts: any) => {\n const appId = requireApp(opts.app);\n const expiresIn = Number(opts.expires);\n if (!Number.isInteger(expiresIn) || expiresIn < 30 || expiresIn > 3600) {\n console.error(chalk.red(\"--expires must be a whole number of seconds between 30 and 3600.\"));\n process.exit(1);\n }\n\n const signed = await client().storage.presign(appId, key, {\n method: opts.upload ? \"put\" : \"get\",\n expiresIn,\n download: opts.download,\n });\n console.log(signed.url);\n // Said plainly because the URL looks like an ordinary link and is not one:\n // anyone who holds it has that access without logging in.\n console.error(\n chalk.yellow(\n `This ${signed.method} link works for ${expiresIn}s for anyone who has it.`,\n ),\n );\n });\n\nstorageCommand\n .command(\"usage\")\n .description(\"Total objects and bytes stored\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (opts: { app?: string }) => {\n const appId = requireApp(opts.app);\n const { objects, bytes } = await client().storage.usage(appId);\n console.log(`${objects} object(s), ${human(bytes)}`);\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\n/**\n * `videan email` -- send mail as an app, through the platform's verified domain.\n *\n * There is no `--from`. Mail goes out as `app-<slug>@<platform domain>`, which is\n * what keeps an app from sending as the platform's own `no-reply@` -- the address\n * password resets come from. `--reply-to` is the lever for routing replies\n * somewhere the app owns.\n */\nexport const emailCommand = new Command(\"email\").description(\"Send email from an app\");\n\nfunction client() {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n return new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n}\n\nfunction requireApp(opt?: string): string {\n const appId = opt || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or link an app.\"));\n process.exit(1);\n }\n return appId;\n}\n\nemailCommand\n .command(\"send\")\n .description(\"Send an email\")\n .requiredOption(\"-t, --to <address...>\", \"Recipient address (repeatable)\")\n .requiredOption(\"-s, --subject <subject>\", \"Subject line\")\n .option(\"--text <body>\", \"Plain-text body\")\n .option(\"--html <body>\", \"HTML body\")\n .option(\"--body-file <path>\", \"Read the body from a file\")\n .option(\"--reply-to <address>\", \"Where replies should go\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"--json\", \"Print the raw response\")\n .action(\n async (opts: {\n to: string[];\n subject: string;\n text?: string;\n html?: string;\n bodyFile?: string;\n replyTo?: string;\n app?: string;\n json?: boolean;\n }) => {\n const appId = requireApp(opts.app);\n\n let text = opts.text;\n let html = opts.html;\n if (opts.bodyFile) {\n const contents = await fs.readFile(opts.bodyFile, \"utf-8\");\n // The extension decides which part it becomes: sending HTML as the plain\n // part delivers visible markup, which looks like a broken app rather than\n // a wrong flag.\n if (/\\.html?$/i.test(opts.bodyFile)) html = contents;\n else text = contents;\n }\n if (!text && !html) {\n console.error(chalk.red(\"Nothing to send. Pass --text, --html or --body-file.\"));\n process.exit(1);\n }\n\n const sent = await client().email.send(appId, {\n to: opts.to,\n subject: opts.subject,\n text,\n html,\n replyTo: opts.replyTo,\n });\n\n if (opts.json) {\n console.log(JSON.stringify(sent, null, 2));\n return;\n }\n\n console.log(chalk.green(\"Sent\"));\n console.log(` From: ${sent.from}`);\n console.log(` To: ${sent.to.join(\", \")}`);\n console.log(` Message: ${sent.messageId ?? \"(no id returned)\"}`);\n // Printed on every send: the quota is per day and shared with nothing else,\n // so the moment to notice it is running low is before it runs out.\n console.log(\n chalk.dim(\n ` Quota: ${sent.quota.sent}/${sent.quota.quota} today, resets ${sent.quota.resetsAt}`,\n ),\n );\n },\n );\n\nemailCommand\n .command(\"quota\")\n .description(\"Show today's sending allowance\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts: { app?: string; json?: boolean }) => {\n const quota = await client().email.quota(requireApp(opts.app));\n if (opts.json) {\n console.log(JSON.stringify(quota, null, 2));\n return;\n }\n console.log(`Sent today: ${quota.sent}`);\n console.log(`Daily quota: ${quota.quota}`);\n console.log(`Remaining: ${quota.remaining}`);\n console.log(chalk.dim(`Resets at: ${quota.resetsAt}`));\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { CloudClient } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\n/**\n * `videan jobs` -- the background queue.\n *\n * A job is a POST the platform makes to a path on the app itself, so there is no\n * worker to deploy: the handler is a route in the app, and `videan jobs enqueue`\n * is a way to trigger it out of band. Retries mean a handler can see the same job\n * twice -- `--key` is how a caller stops its *own* retry from becoming two jobs.\n */\nexport const jobsCommand = new Command(\"jobs\").description(\"Enqueue and inspect background jobs\");\n\nfunction client() {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n return new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n}\n\nfunction requireApp(opt?: string): string {\n const appId = opt || getCurrentApp();\n if (!appId) {\n console.error(chalk.red(\"No app specified. Use --app or run `videan init` first.\"));\n process.exit(1);\n }\n return appId;\n}\n\n/** Parse a numeric flag, failing on junk rather than sending NaN to the API. */\nfunction intOption(flag: string, raw: string): number {\n const n = Number(raw);\n if (!Number.isInteger(n) || n < 0) {\n console.error(chalk.red(`${flag} expects a whole number, got \"${raw}\".`));\n process.exit(1);\n }\n return n;\n}\n\nconst STATUS_COLOR: Record<string, (s: string) => string> = {\n queued: chalk.yellow,\n running: chalk.cyan,\n succeeded: chalk.green,\n dead: chalk.red,\n cancelled: chalk.gray,\n};\n\nfunction statusLabel(status: string): string {\n return (STATUS_COLOR[status] ?? chalk.white)(status);\n}\n\njobsCommand\n .command(\"enqueue\")\n .description(\"Queue a job for the app to run\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-q, --queue <name>\", \"Queue name (default: default)\")\n .option(\"-p, --path <path>\", \"Path on the app to POST to (default: /jobs)\")\n .option(\"-d, --data <json>\", \"JSON payload\")\n .option(\"-f, --file <path>\", \"Read the JSON payload from a file\")\n .option(\"--delay <seconds>\", \"Run no earlier than this many seconds from now\")\n .option(\"--attempts <n>\", \"Attempts before the job is given up on (default: 3)\")\n .option(\"--timeout <seconds>\", \"How long the handler gets per attempt (default: 60)\")\n .option(\"--key <key>\", \"Idempotency key -- a repeat returns the same job\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts) => {\n const appId = requireApp(opts.app);\n\n // Read from a file or a flag, and refuse invalid JSON here rather than letting\n // the API reject it: the payload is usually the thing being iterated on, and a\n // round trip to say \"bad JSON\" is a slow way to learn about a typo.\n let payload: unknown;\n const raw = opts.file ? await fs.readFile(opts.file, \"utf-8\") : opts.data;\n if (raw !== undefined) {\n try {\n payload = JSON.parse(raw);\n } catch (e: any) {\n console.error(chalk.red(`--${opts.file ? \"file\" : \"data\"} is not valid JSON: ${e.message}`));\n process.exit(1);\n }\n }\n\n const job = await client().jobs.enqueue(appId, {\n queue: opts.queue,\n path: opts.path,\n payload,\n delaySeconds: opts.delay !== undefined ? intOption(\"--delay\", opts.delay) : undefined,\n maxAttempts: opts.attempts !== undefined ? intOption(\"--attempts\", opts.attempts) : undefined,\n timeoutSeconds: opts.timeout !== undefined ? intOption(\"--timeout\", opts.timeout) : undefined,\n idempotencyKey: opts.key,\n });\n\n if (opts.json) {\n console.log(JSON.stringify(job, null, 2));\n return;\n }\n\n if (job.deduplicated) {\n // Said plainly, because the alternative reading -- that the job was queued\n // again -- is the one that leads to work being counted twice.\n console.log(chalk.yellow(`Already queued as ${job.id} (idempotency key \"${opts.key}\")`));\n } else {\n console.log(chalk.green(`Queued ${job.id}`));\n }\n console.log(chalk.gray(` ${job.method} ${job.path} on queue \"${job.queue}\"`));\n if (job.runAt && Date.parse(job.runAt) > Date.now() + 1000) {\n console.log(chalk.gray(` Runs at ${new Date(job.runAt).toLocaleString()}`));\n }\n });\n\njobsCommand\n .command(\"list\")\n .description(\"List jobs, newest first\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"-s, --status <status>\", \"queued, running, succeeded, dead or cancelled\")\n .option(\"-q, --queue <name>\", \"Only this queue\")\n .option(\"-n, --limit <n>\", \"How many to show (default: 20)\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts) => {\n const appId = requireApp(opts.app);\n const { jobs, counts } = await client().jobs.list(appId, {\n status: opts.status,\n queue: opts.queue,\n limit: opts.limit !== undefined ? intOption(\"--limit\", opts.limit) : 20,\n });\n\n if (opts.json) {\n console.log(JSON.stringify({ jobs, counts }, null, 2));\n return;\n }\n\n // Depth first: a page of 20 cannot say how far behind the queue is, and that\n // is the question someone running this is usually asking.\n const depth = Object.entries(counts)\n .map(([status, n]) => `${statusLabel(status)} ${n}`)\n .join(\" \");\n console.log(depth || chalk.gray(\"No jobs yet.\"));\n\n if (jobs.length === 0) return;\n console.log();\n for (const job of jobs) {\n const attempts = job.attempts > 0 ? chalk.gray(` ${job.attempts}/${job.maxAttempts}`) : \"\";\n console.log(\n `${statusLabel(job.status).padEnd(20)} ${chalk.white(job.id)} ${chalk.gray(\n `${job.queue} ${job.path}`,\n )}${attempts}`,\n );\n // Only the last error, and only when there is one: a queued job with an\n // error is a job that failed and is waiting to be retried, which reads as\n // healthy without it.\n if (job.error) console.log(chalk.red(` ${job.error}`));\n }\n });\n\njobsCommand\n .command(\"get <job-id>\")\n .description(\"Show one job in full\")\n .option(\"-a, --app <id>\", \"App ID\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (jobId, opts) => {\n const job = await client().jobs.get(requireApp(opts.app), jobId);\n if (opts.json) {\n console.log(JSON.stringify(job, null, 2));\n return;\n }\n console.log(`${chalk.white(job.id)} ${statusLabel(job.status)}`);\n console.log(chalk.gray(` ${job.method} ${job.path} on queue \"${job.queue}\"`));\n console.log(chalk.gray(` Attempts: ${job.attempts}/${job.maxAttempts}`));\n if (job.statusCode) console.log(chalk.gray(` Last response: HTTP ${job.statusCode}`));\n if (job.durationMs) console.log(chalk.gray(` Took: ${job.durationMs}ms`));\n if (job.error) console.log(chalk.red(` Error: ${job.error}`));\n if (job.status === \"queued\" && job.attempts > 0) {\n console.log(chalk.yellow(` Retrying at ${new Date(job.runAt).toLocaleString()}`));\n }\n if (job.payload !== null && job.payload !== undefined) {\n console.log(chalk.gray(\" Payload:\"));\n console.log(\n JSON.stringify(job.payload, null, 2)\n .split(\"\\n\")\n .map((l) => ` ${l}`)\n .join(\"\\n\"),\n );\n }\n });\n\njobsCommand\n .command(\"retry <job-id>\")\n .description(\"Run a dead job again, from a clean attempt count\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (jobId, opts) => {\n const job = await client().jobs.retry(requireApp(opts.app), jobId);\n console.log(chalk.green(`Requeued ${job.id}`));\n });\n\njobsCommand\n .command(\"cancel <job-id>\")\n .description(\"Cancel a job that has not started yet\")\n .option(\"-a, --app <id>\", \"App ID\")\n .action(async (jobId, opts) => {\n const job = await client().jobs.cancel(requireApp(opts.app), jobId);\n console.log(chalk.green(`Cancelled ${job.id}`));\n });\n", "import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { CloudClient, type UsageQuantity } from \"@videan/sdk\";\nimport { getApiKey, getBaseUrl, getCurrentApp, getDefaultOrg } from \"../config.js\";\nimport { requireApiKey } from \"../util.js\";\n\n/**\n * `videan usage` -- what the month costs, and where it went.\n *\n * Org-level by default, because that is the number that gets invoiced; `--app`\n * narrows to one app's share. The two do not add up to the same figure on purpose:\n * the plan's included allowance applies once to the org, so per-app lines are list\n * price with no allowance and the org total subtracts it exactly once.\n *\n * Nothing here changes a plan or pays anything. `videan usage plans` prints the\n * catalogue so an upgrade can be priced before anyone talks to a card.\n */\nexport const usageCommand = new Command(\"usage\")\n .description(\"Metered usage and cost for the current month\")\n .option(\"-o, --org <id>\", \"Organization ID\")\n .option(\"-a, --app <id>\", \"Show one app's share instead of the whole org\")\n .option(\"-p, --period <YYYY-MM>\", \"Month to report on (default: the month in progress)\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts) => {\n const apiKey = getApiKey();\n requireApiKey(apiKey);\n const client = new CloudClient({ apiKey, baseUrl: getBaseUrl() });\n\n if (opts.app || (!opts.org && getCurrentApp() && !getDefaultOrg())) {\n const appId = opts.app || getCurrentApp()!;\n const report = await client.apps.usage(appId, opts.period);\n if (opts.json) return void console.log(JSON.stringify(report, null, 2));\n\n console.log(chalk.dim(`${period(report.periodStart, report.periodEnd)} app ${appId}`));\n console.log();\n printMetrics(report.metrics);\n console.log();\n for (const line of report.lines) {\n console.log(` ${chalk.dim(line.label.padEnd(22))} ${money(line.cents)}`);\n }\n console.log(` ${chalk.bold(\"Share of the bill\".padEnd(22))} ${chalk.bold(report.cost)}`);\n console.log();\n console.log(\n chalk.dim(\"At list price, with no plan allowance applied \u2014 see `videan usage` for the org total.\"),\n );\n return;\n }\n\n const orgId = opts.org || getDefaultOrg();\n if (!orgId) {\n console.error(chalk.red(\"No org specified. Use --org, or --app for a single app.\"));\n process.exit(1);\n }\n\n const report = await client.orgs.usage(orgId, opts.period);\n if (opts.json) return void console.log(JSON.stringify(report, null, 2));\n\n const inv = report.invoice;\n console.log(\n chalk.dim(`${period(report.periodStart, report.periodEnd)} org ${orgId} plan ${report.plan}`),\n );\n console.log();\n printMetrics(report.metrics);\n\n if (report.apps.length > 0) {\n console.log();\n console.log(chalk.bold(\"By app\"));\n // Descending, because the question is which app is costing the money.\n for (const app of [...report.apps].sort((a, b) => b.cents - a.cents)) {\n const name = app.slug || app.name || app.appId || \"(deleted apps)\";\n console.log(` ${chalk.dim(String(name).padEnd(24))} ${money(app.cents)}`);\n }\n }\n\n console.log();\n for (const line of inv.lines) {\n console.log(\n ` ${chalk.dim(line.label.padEnd(22))} ${money(line.cents)} ${chalk.dim(\n `(${line.quantity} \u00D7 ${line.unitPriceCents}\u00A2)`,\n )}`,\n );\n }\n console.log(` ${chalk.dim(\"Usage\".padEnd(22))} ${money(inv.usageCents)}`);\n if (inv.includedUsageCents > 0) {\n console.log(\n ` ${chalk.dim(\"Included\".padEnd(22))} ${chalk.green(`-${money(Math.min(inv.usageCents, inv.includedUsageCents))}`)}`,\n );\n }\n if (inv.baseCents > 0) {\n console.log(` ${chalk.dim(\"Plan\".padEnd(22))} ${money(inv.baseCents)}`);\n }\n console.log(` ${chalk.bold(\"Total\".padEnd(22))} ${chalk.bold(report.total)}`);\n console.log();\n // Said explicitly: the month is not over, so this is what has accrued rather\n // than what will be charged.\n console.log(chalk.dim(\"Usage to date. The period is priced as it stands right now.\"));\n });\n\nusageCommand\n .command(\"plans\")\n .description(\"The plan catalogue and what each one allows\")\n .option(\"--json\", \"Print the raw response\")\n .action(async (opts) => {\n // No key needed by the API, but the client requires one; an empty string is\n // enough and keeps `videan usage plans` working before login.\n const client = new CloudClient({ apiKey: getApiKey() || \"\", baseUrl: getBaseUrl() });\n const plans = await client.plans();\n if (opts.json) return void console.log(JSON.stringify(plans, null, 2));\n\n for (const plan of plans) {\n console.log(\n `${chalk.bold(plan.name)} ${chalk.dim(`(${plan.id})`)} ${plan.base}/mo` +\n (plan.includedUsageCents > 0 ? chalk.dim(`, ${plan.includedUsage} usage included`) : \"\"),\n );\n const l = plan.limits;\n console.log(\n chalk.dim(\n ` ${l.apps} apps \u00B7 ${l.instancesPerApp} instances/app \u00B7 up to ${l.maxCpu} CPU / ${l.maxMemory} MB \u00B7 ${l.maxStorageMb} MB storage` +\n `${l.postgres ? \" \u00B7 postgres\" : \"\"}${l.customDomains ? \" \u00B7 custom domains\" : \"\"}`,\n ),\n );\n console.log();\n }\n });\n\nfunction printMetrics(metrics: UsageQuantity[]): void {\n if (metrics.length === 0) {\n console.log(chalk.dim(\" Nothing metered in this period yet.\"));\n return;\n }\n for (const m of metrics) {\n console.log(` ${chalk.dim(m.metric.padEnd(22))} ${round(m.quantity)}`);\n }\n}\n\n/** Two decimals, and no trailing `.00` on a whole number. */\nfunction round(n: number): string {\n const v = Math.round(n * 100) / 100;\n return Number.isInteger(v) ? String(v) : v.toFixed(2);\n}\n\nfunction money(cents: number): string {\n return `$${(cents / 100).toFixed(2)}`;\n}\n\nfunction period(start: string, end: string): string {\n return `${start.slice(0, 10)} \u2192 ${end.slice(0, 10)}`;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["Command", "inquirer", "chalk", "path", "client", "period", "DEFAULT_API_URL", "APP_DOMAIN", "MAX_FILE_SIZE_BYTES", "APP_PORT", "LAMBDA_ADAPTER", "APP_PORT", "FARGATE_SIZES", "FARGATE_CPUS", "PLANS", "done", "chalk", "inquirer", "DEFAULT_API_URL", "spinner", "token", "Command", "chalk", "Command", "chalk", "Command", "inquirer", "chalk", "fs", "path", "Command", "configPath", "path", "fs", "chalk", "inquirer", "config", "Command", "chalk", "ora", "fs", "path", "chalk", "Command", "chalk", "client", "ora", "Command", "chalk", "ora", "fs", "path", "Command", "DEFAULT_API_URL", "ora", "chalk", "path", "fs", "Command", "chalk", "Command", "chalk", "client", "Command", "chalk", "Command", "chalk", "client", "Command", "chalk", "ora", "Command", "chalk", "client", "ora", "Command", "chalk", "Command", "chalk", "client", "Command", "chalk", "Command", "chalk", "client", "Command", "chalk", "Command", "chalk", "client", "Command", "chalk", "Command", "chalk", "client", "Command", "chalk", "Command", "client", "chalk", "Command", "chalk", "fs", "path", "Command", "chalk", "units", "fs", "path", "Command", "chalk", "fs", "Command", "client", "requireApp", "chalk", "fs", "Command", "chalk", "fs", "Command", "client", "requireApp", "chalk", "intOption", "fs", "Command", "chalk", "Command", "client", "report", "chalk", "Command"]
7
7
  }