@userbox/cli 0.1.0-alpha.0 → 0.1.0-alpha.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to `@userbox/cli` are documented in this file.
4
4
 
5
+ ## [0.1.0-alpha.1] — 2026-11-04
6
+
7
+ ### Changed
8
+ - `apply` and `dev seed`: a plan that removes permissions from an existing
9
+ role always needs a human answer at a terminal. `--yes` no longer skips it,
10
+ and without a terminal the command stops with `NEEDS_HUMAN`. Purely additive
11
+ plans still run with `--yes`.
12
+
5
13
  ## [0.1.0-alpha.0] — 2026-11-04
6
14
 
7
15
  ### Added
package/README.md CHANGED
@@ -26,7 +26,9 @@ Secrets never pass through the model:
26
26
  run `npx @userbox/cli login` in their own terminal.
27
27
  - `init` writes the API key straight to `.env.local` and never prints it.
28
28
  - `apply` without `--yes` only prints the plan when there is no terminal: show
29
- it to the human, then run `apply --yes`.
29
+ it to the human, then run `apply --yes`. A plan that removes permissions from
30
+ an existing role fails with `NEEDS_HUMAN` even with `--yes`: the human runs
31
+ `npx @userbox/cli apply` in their own terminal.
30
32
 
31
33
  ## Commands
32
34
 
@@ -35,7 +37,7 @@ Secrets never pass through the model:
35
37
  | `login` | Logs in as a tenant admin (a user with console access). Stores the session in `~/.config/userbox/credentials.json`, mode 600. `--api-url` picks another API. |
36
38
  | `logout` / `whoami` | Forget / show the login. |
37
39
  | `init` | Needs Next.js with `app/` or `src/app/`. Adds the `--origin` host (default `http://localhost:3000`) to the allowed redirect domains, creates an API key with `access:check`, `memberships:read`, `workspaces:read` unless `.env.local` already has one, merges the five variables into `.env.local` without touching existing values (`--force` replaces them), makes sure `.env.local` is git-ignored, installs `@userbox/react@alpha` and `@userbox/node@alpha` (`--skip-install` skips it), and writes `providers.tsx`, `auth/callback/page.tsx` and `api/auth/refresh/route.ts` if they do not exist. It wraps `{children}` in the root layout with `<Providers>` when that edit is unambiguous. |
38
- | `apply` | Reads `userbox.yaml`, creates missing permissions, and makes every declared role in every active workspace hold exactly its listed permissions. Permissions and roles that are not in the file are reported, never deleted. |
40
+ | `apply` | Reads `userbox.yaml`, creates missing permissions, and makes every declared role in every active workspace hold exactly its listed permissions. Permissions and roles that are not in the file are reported, never deleted. Removing a permission from an existing role needs a human at a terminal, even with `--yes`. |
39
41
  | `dev seed` | Creates (or reuses) the workspace `dev`, syncs `userbox.yaml` into it and gives you the `admin` role. `--invite email[:role]` invites more users by email (role defaults to `member`). Warns when the default environment is a production environment. |
40
42
  | `doctor` | Checks the project, SDK versions, auth files, `.env.local`, secrets in `NEXT_PUBLIC_*`, `.gitignore`, the API and JWKS, the API key's tenant and `access:check` scope, and, when logged in, the redirect domain and sign-up mode. Exits 1 when a check fails. |
41
43
 
package/dist/bin.js CHANGED
@@ -282,6 +282,9 @@ function planApply(manifest, remote) {
282
282
  function planIsEmpty(plan) {
283
283
  return plan.createPermissions.length === 0 && plan.createRoles.length === 0 && plan.grant.length === 0 && plan.revoke.length === 0;
284
284
  }
285
+ function planRevokes(plan) {
286
+ return plan.revoke.length > 0;
287
+ }
285
288
 
286
289
  // src/prompt.ts
287
290
  import { createInterface } from "readline";
@@ -419,6 +422,25 @@ function revokeRolePermission(s, env, workspaceId, roleId, permissionId) {
419
422
  }
420
423
 
421
424
  // src/commands/apply.ts
425
+ function isInteractive(flags) {
426
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY) && !flags.json;
427
+ }
428
+ async function confirmRevokes(sync, flags, command) {
429
+ if (!sync.plans.some((p) => planRevokes(p.plan))) return true;
430
+ const lines = describe(sync).filter((l) => l.startsWith(" - "));
431
+ if (!isInteractive(flags)) {
432
+ throw new CliError(
433
+ "NEEDS_HUMAN",
434
+ `the plan removes permissions from existing roles:
435
+ ${lines.join("\n")}`,
436
+ `Ask the human to review it and run \`npx @userbox/cli ${command}\` in their own terminal.`
437
+ );
438
+ }
439
+ process.stdout.write(`This removes permissions from existing roles:
440
+ ${lines.join("\n")}
441
+ `);
442
+ return confirm("Remove them?");
443
+ }
422
444
  function loadManifest(root) {
423
445
  const path = join2(root, "userbox.yaml");
424
446
  if (!existsSync(path)) {
@@ -501,8 +523,7 @@ async function apply(flags, out) {
501
523
  return;
502
524
  }
503
525
  if (!flags.yes) {
504
- const interactive = process.stdin.isTTY && process.stdout.isTTY && !flags.json;
505
- if (!interactive) {
526
+ if (!isInteractive(flags)) {
506
527
  out.done({ ...summary, applied: false, hint: "Re-run with --yes to apply." }, [
507
528
  "Plan:",
508
529
  ...changes,
@@ -517,6 +538,9 @@ ${changes.join("\n")}
517
538
  out.done({ ...summary, applied: false }, ["Nothing applied."]);
518
539
  return;
519
540
  }
541
+ } else if (!await confirmRevokes(sync, flags, "apply")) {
542
+ out.done({ ...summary, applied: false }, ["Nothing applied."]);
543
+ return;
520
544
  }
521
545
  await executeSync(session, env, sync);
522
546
  out.done({ ...summary, applied: true }, ["Applied:", ...changes]);
@@ -541,8 +565,12 @@ async function devSeed(flags, out) {
541
565
  }
542
566
  if (existsSync2(join3(process.cwd(), "userbox.yaml"))) {
543
567
  const sync = await syncManifest(session, env, loadManifest(process.cwd()), [ws]);
544
- await executeSync(session, env, sync);
545
- out.step("Synced userbox.yaml roles into the workspace");
568
+ if (await confirmRevokes(sync, flags, "dev seed")) {
569
+ await executeSync(session, env, sync);
570
+ out.step("Synced userbox.yaml roles into the workspace");
571
+ } else {
572
+ out.warn("Skipped the userbox.yaml sync: nothing was removed from the roles.");
573
+ }
546
574
  } else {
547
575
  out.warn("No userbox.yaml: the workspace has no roles yet. Add one and run `userbox apply`.");
548
576
  }
@@ -1044,7 +1072,7 @@ async function whoami(out) {
1044
1072
  }
1045
1073
 
1046
1074
  // src/bin.ts
1047
- var VERSION = "0.1.0-alpha.0";
1075
+ var VERSION = "0.1.0-alpha.1";
1048
1076
  var HELP = `userbox \u2014 set up Userbox in your app
1049
1077
 
1050
1078
  Usage: userbox <command> [options]
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bin.ts","../src/commands/apply.ts","../src/credentials.ts","../src/output.ts","../src/api.ts","../src/manifest.ts","../src/prompt.ts","../src/userbox.ts","../src/commands/dev-seed.ts","../src/commands/doctor.ts","../src/envfile.ts","../src/project.ts","../src/commands/init.ts","../src/scaffold.ts","../src/commands/login.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\n\nimport { apply } from \"./commands/apply.js\";\nimport { devSeed } from \"./commands/dev-seed.js\";\nimport { doctor } from \"./commands/doctor.js\";\nimport { init } from \"./commands/init.js\";\nimport { login, logout, whoami } from \"./commands/login.js\";\nimport { CliError, createOutput, printError } from \"./output.js\";\n\nconst VERSION = \"0.1.0-alpha.0\";\n\nconst HELP = `userbox — set up Userbox in your app\n\nUsage: userbox <command> [options]\n\nCommands\n login Log in as a tenant admin (interactive, human only)\n logout Forget the local login\n whoami Show the current login\n init Set up this Next.js app: env, API key, SDKs, auth files\n apply Sync userbox.yaml (permissions and roles) to the environment\n dev seed Create a dev workspace with an admin and a member user\n doctor Check this app's Userbox setup\n\nOptions\n --json Machine-readable output (for coding agents)\n --yes, -y Apply without asking (apply)\n --force init: replace existing USERBOX_* values in .env.local\n --origin <url> init: the app origin (default http://localhost:3000)\n --skip-install init: do not install the SDKs\n --api-url <url> login: the Userbox API (default https://api.test.userbox.app)\n --invite <email[:role]> dev seed: invite a user (repeatable, role defaults to member)\n --help, -h Show this help\n --version, -v Show the version\n\nDocs: https://docs.test.userbox.app/llms.txt`;\n\nexport interface Flags {\n json: boolean;\n yes: boolean;\n force: boolean;\n origin?: string;\n skipInstall: boolean;\n apiUrl?: string;\n invite: string[];\n}\n\nasync function main(argv: string[]): Promise<number> {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n strict: true,\n options: {\n json: { type: \"boolean\", default: false },\n yes: { type: \"boolean\", short: \"y\", default: false },\n force: { type: \"boolean\", default: false },\n origin: { type: \"string\" },\n \"skip-install\": { type: \"boolean\", default: false },\n \"api-url\": { type: \"string\" },\n invite: { type: \"string\", multiple: true, default: [] },\n help: { type: \"boolean\", short: \"h\", default: false },\n version: { type: \"boolean\", short: \"v\", default: false },\n },\n });\n const flags: Flags = {\n json: values.json,\n yes: values.yes,\n force: values.force,\n origin: values.origin,\n skipInstall: values[\"skip-install\"],\n apiUrl: values[\"api-url\"],\n invite: values.invite,\n };\n if (values.version) {\n process.stdout.write(`${VERSION}\\n`);\n return 0;\n }\n const command = positionals.join(\" \");\n if (values.help || command === \"\") {\n process.stdout.write(`${HELP}\\n`);\n return 0;\n }\n const out = createOutput(flags.json);\n try {\n switch (command) {\n case \"login\":\n await login(flags, out);\n break;\n case \"logout\":\n logout(out);\n break;\n case \"whoami\":\n await whoami(out);\n break;\n case \"init\":\n await init(flags, out);\n break;\n case \"apply\":\n await apply(flags, out);\n break;\n case \"dev seed\":\n await devSeed(flags, out);\n break;\n case \"doctor\":\n return await doctor(flags, out);\n default:\n throw new CliError(\"UNKNOWN_COMMAND\", `unknown command \"${command}\"`, \"Run `userbox --help`.\");\n }\n return 0;\n } catch (err) {\n printError(err, flags.json);\n return 1;\n }\n}\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (err) => {\n printError(err, process.argv.includes(\"--json\"));\n process.exit(1);\n },\n);\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { Session } from \"../api.js\";\nimport type { Flags } from \"../bin.js\";\nimport { type Manifest, type Plan, parseManifest, planApply, planIsEmpty } from \"../manifest.js\";\nimport { CliError, type Output } from \"../output.js\";\nimport { confirm } from \"../prompt.js\";\nimport {\n type Environment,\n type Workspace,\n createPermission,\n createRole,\n defaultEnvironment,\n grantRolePermission,\n listExternalPermissions,\n listRolePermissions,\n listRoles,\n listWorkspaces,\n revokeRolePermission,\n} from \"../userbox.js\";\n\n// Permissions live in the environment; roles live in each workspace. `apply`\n// creates the permissions once and syncs the roles into every active\n// workspace of the default environment.\n\nexport function loadManifest(root: string): Manifest {\n const path = join(root, \"userbox.yaml\");\n if (!existsSync(path)) {\n throw new CliError(\n \"NO_MANIFEST\",\n \"no userbox.yaml in this directory\",\n \"Create one:\\n permissions: [projects.read, projects.delete]\\n roles:\\n admin: [projects.*]\\n member: [projects.read]\",\n );\n }\n return parseManifest(readFileSync(path, \"utf8\"));\n}\n\ninterface WorkspacePlan {\n workspace: Workspace;\n plan: Plan;\n roleIds: Map<string, string>;\n}\n\nasync function workspacePlan(s: Session, env: Environment, manifest: Manifest, ws: Workspace, envPerms: string[]) {\n const roles = await listRoles(s, env, ws.id);\n const remoteRoles: Record<string, string[]> = {};\n const roleIds = new Map<string, string>();\n for (const role of roles) {\n roleIds.set(role.name, role.id);\n const perms = await listRolePermissions(s, env, ws.id, role.id);\n remoteRoles[role.name] = perms.filter((p) => p.type === \"external\").map((p) => p.key);\n }\n return { workspace: ws, plan: planApply(manifest, { permissions: envPerms, roles: remoteRoles }), roleIds };\n}\n\n/** Make the workspace's roles match the manifest. Permissions must already exist. */\nexport async function executeRoles(\n s: Session,\n env: Environment,\n wp: WorkspacePlan,\n permissionIds: Map<string, string>,\n): Promise<void> {\n const ids = new Map(wp.roleIds);\n for (const name of wp.plan.createRoles) {\n ids.set(name, (await createRole(s, env, wp.workspace.id, name)).id);\n }\n for (const { role, permission } of wp.plan.grant) {\n await grantRolePermission(s, env, wp.workspace.id, ids.get(role)!, permissionIds.get(permission)!);\n }\n for (const { role, permission } of wp.plan.revoke) {\n const pid = permissionIds.get(permission);\n if (pid) await revokeRolePermission(s, env, wp.workspace.id, ids.get(role)!, pid);\n }\n}\n\nexport async function syncManifest(\n s: Session,\n env: Environment,\n manifest: Manifest,\n workspaces: Workspace[],\n): Promise<{ createdPermissions: string[]; plans: WorkspacePlan[]; permissionIds: Map<string, string> }> {\n const existing = await listExternalPermissions(s, env);\n const permissionIds = new Map(existing.map((p) => [p.key, p.id]));\n const plans: WorkspacePlan[] = [];\n for (const ws of workspaces) plans.push(await workspacePlan(s, env, manifest, ws, [...permissionIds.keys()]));\n return { createdPermissions: manifest.permissions.filter((p) => !permissionIds.has(p)), plans, permissionIds };\n}\n\nexport async function executeSync(\n s: Session,\n env: Environment,\n sync: Awaited<ReturnType<typeof syncManifest>>,\n): Promise<void> {\n for (const key of sync.createdPermissions) {\n sync.permissionIds.set(key, (await createPermission(s, env, key)).id);\n }\n for (const wp of sync.plans) await executeRoles(s, env, wp, sync.permissionIds);\n}\n\nfunction describe(sync: Awaited<ReturnType<typeof syncManifest>>): string[] {\n const lines: string[] = [];\n for (const p of sync.createdPermissions) lines.push(` + permission ${p}`);\n for (const { workspace, plan } of sync.plans) {\n const label = workspace.slug ?? workspace.name;\n for (const r of plan.createRoles) lines.push(` + role ${r} in ${label}`);\n for (const g of plan.grant) lines.push(` + ${g.role} → ${g.permission} in ${label}`);\n for (const r of plan.revoke) lines.push(` - ${r.role} → ${r.permission} in ${label}`);\n }\n return lines;\n}\n\nexport async function apply(flags: Flags, out: Output): Promise<void> {\n const manifest = loadManifest(process.cwd());\n const session = Session.load();\n const env = await defaultEnvironment(session);\n const workspaces = await listWorkspaces(session, env);\n if (workspaces.length === 0) {\n out.warn(\"No workspaces yet: roles are created per workspace. Run `userbox dev seed` to create one.\");\n }\n const sync = await syncManifest(session, env, manifest, workspaces);\n const changes = describe(sync);\n const extras = new Set(sync.plans.flatMap((p) => p.plan.extraPermissions));\n if (extras.size > 0) out.warn(`Not in userbox.yaml, left as they are: ${[...extras].join(\", \")}`);\n const empty = sync.createdPermissions.length === 0 && sync.plans.every((p) => planIsEmpty(p.plan));\n const summary = {\n environment: { id: env.id, name: env.name },\n workspaces: workspaces.map((w) => w.slug ?? w.id),\n changes,\n };\n if (empty) {\n out.done({ ...summary, applied: false, upToDate: true }, [\"Up to date: nothing to apply.\"]);\n return;\n }\n if (!flags.yes) {\n const interactive = process.stdin.isTTY && process.stdout.isTTY && !flags.json;\n if (!interactive) {\n out.done({ ...summary, applied: false, hint: \"Re-run with --yes to apply.\" }, [\n \"Plan:\",\n ...changes,\n \"Re-run with --yes to apply.\",\n ]);\n return;\n }\n process.stdout.write(`Plan for ${env.name}:\\n${changes.join(\"\\n\")}\\n`);\n if (!(await confirm(\"Apply these changes?\"))) {\n out.done({ ...summary, applied: false }, [\"Nothing applied.\"]);\n return;\n }\n }\n await executeSync(session, env, sync);\n out.done({ ...summary, applied: true }, [\"Applied:\", ...changes]);\n}\n","import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n// The CLI login is a normal tenant-admin session: a 15-minute access token and\n// a single-use refresh token, rotated on every refresh. Both live in one file,\n// mode 600, in a mode-700 directory. Nothing else is ever written there.\n\nexport interface Credentials {\n apiUrl: string;\n tenantSlug: string;\n tenantId: string;\n email: string;\n accessToken: string;\n refreshToken: string;\n /** Epoch milliseconds. */\n accessExpiresAt: number;\n}\n\nexport function configDir(): string {\n const base = process.env.XDG_CONFIG_HOME || join(homedir(), \".config\");\n return join(base, \"userbox\");\n}\n\nfunction credentialsPath(): string {\n return join(configDir(), \"credentials.json\");\n}\n\nexport function loadCredentials(): Credentials | null {\n try {\n return JSON.parse(readFileSync(credentialsPath(), \"utf8\")) as Credentials;\n } catch {\n return null;\n }\n}\n\nexport function saveCredentials(creds: Credentials): void {\n const dir = configDir();\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n chmodSync(dir, 0o700);\n const path = credentialsPath();\n writeFileSync(path, `${JSON.stringify(creds, null, 2)}\\n`, { mode: 0o600 });\n chmodSync(path, 0o600);\n}\n\nexport function clearCredentials(): void {\n rmSync(credentialsPath(), { force: true });\n}\n","// Every command prints for two readers: a human, or (with --json) a coding\n// agent. Errors always carry a stable code and a hint that says what to do.\n\nexport class CliError extends Error {\n constructor(\n readonly code: string,\n message: string,\n readonly hint?: string,\n ) {\n super(message);\n }\n}\n\nexport interface Output {\n json: boolean;\n /** A progress line for humans; silent in --json mode. */\n step(message: string): void;\n /** A warning; kept for the JSON result in --json mode. */\n warn(message: string): void;\n /** The final result: a JSON document, or the human summary lines. */\n done(result: Record<string, unknown>, lines: string[]): void;\n}\n\nexport function createOutput(json: boolean): Output {\n const warnings: string[] = [];\n return {\n json,\n step(message) {\n if (!json) process.stdout.write(` ${message}\\n`);\n },\n warn(message) {\n warnings.push(message);\n if (!json) process.stderr.write(` ! ${message}\\n`);\n },\n done(result, lines) {\n if (json) {\n process.stdout.write(`${JSON.stringify({ ok: true, ...result, warnings }, null, 2)}\\n`);\n } else {\n for (const line of lines) process.stdout.write(`${line}\\n`);\n }\n },\n };\n}\n\nexport function printError(err: unknown, json: boolean): void {\n const e =\n err instanceof CliError\n ? err\n : new CliError(\"UNEXPECTED\", err instanceof Error ? err.message : String(err));\n if (json) {\n process.stdout.write(\n `${JSON.stringify({ ok: false, error: { code: e.code, message: e.message, hint: e.hint ?? null } }, null, 2)}\\n`,\n );\n } else {\n process.stderr.write(`error [${e.code}]: ${e.message}\\n`);\n if (e.hint) process.stderr.write(`hint: ${e.hint}\\n`);\n }\n}\n","import { type Credentials, loadCredentials, saveCredentials } from \"./credentials.js\";\nimport { CliError } from \"./output.js\";\n\nexport const DEFAULT_API_URL = \"https://api.test.userbox.app\";\n\ninterface ApiErrorBody {\n code?: string;\n error?: string;\n detail?: unknown;\n}\n\nexport class ApiError extends CliError {\n constructor(\n readonly status: number,\n code: string,\n message: string,\n ) {\n super(code, `${message} (HTTP ${status})`);\n }\n}\n\nasync function parseError(res: Response): Promise<ApiError> {\n let body: ApiErrorBody = {};\n try {\n body = (await res.json()) as ApiErrorBody;\n } catch {\n // non-JSON error body\n }\n const message =\n body.error ??\n (typeof body.detail === \"string\" ? body.detail : body.detail ? JSON.stringify(body.detail) : res.statusText);\n return new ApiError(res.status, body.code ?? `HTTP_${res.status}`, message);\n}\n\nexport interface RequestOptions {\n method?: string;\n body?: unknown;\n headers?: Record<string, string>;\n}\n\nexport async function rawRequest<T>(apiUrl: string, path: string, opts: RequestOptions = {}): Promise<T> {\n let res: Response;\n try {\n res = await fetch(`${apiUrl.replace(/\\/$/, \"\")}${path}`, {\n method: opts.method ?? \"GET\",\n headers: {\n Accept: \"application/json\",\n ...(opts.body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n ...opts.headers,\n },\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n });\n } catch (err) {\n throw new CliError(\"NETWORK\", `cannot reach ${apiUrl}: ${(err as Error).message}`);\n }\n if (!res.ok) throw await parseError(res);\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n}\n\nexport interface TokenPair {\n access_token: string;\n refresh_token: string;\n expires_in?: number;\n}\n\nexport function expiryFrom(tokens: TokenPair): number {\n return Date.now() + (tokens.expires_in ?? 900) * 1000;\n}\n\n/** An authenticated client bound to the stored CLI login. */\nexport class Session {\n private constructor(private creds: Credentials) {}\n\n static load(): Session {\n const creds = loadCredentials();\n if (!creds) {\n throw new CliError(\n \"NOT_LOGGED_IN\",\n \"not logged in\",\n \"Ask the human to run `npx @userbox/cli login` in their own terminal, then retry.\",\n );\n }\n return new Session(creds);\n }\n\n get apiUrl(): string {\n return this.creds.apiUrl;\n }\n get tenantSlug(): string {\n return this.creds.tenantSlug;\n }\n get tenantId(): string {\n return this.creds.tenantId;\n }\n get email(): string {\n return this.creds.email;\n }\n /** The logged-in user's id: the `sub` of our own access token (not an authorization decision). */\n get userId(): string {\n const payload = this.creds.accessToken.split(\".\")[1] ?? \"\";\n return (JSON.parse(Buffer.from(payload, \"base64url\").toString(\"utf8\")) as { sub: string }).sub;\n }\n\n private async refresh(): Promise<void> {\n try {\n const tokens = await rawRequest<TokenPair>(this.creds.apiUrl, \"/v1/auth/refresh\", {\n method: \"POST\",\n body: { refresh_token: this.creds.refreshToken },\n headers: { \"X-Tenant-ID\": this.creds.tenantId },\n });\n this.creds = {\n ...this.creds,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n accessExpiresAt: expiryFrom(tokens),\n };\n saveCredentials(this.creds);\n } catch (err) {\n if (err instanceof ApiError && err.status >= 400 && err.status < 500) {\n throw new CliError(\n \"SESSION_EXPIRED\",\n \"the CLI login has expired\",\n \"Ask the human to run `npx @userbox/cli login` again.\",\n );\n }\n throw err;\n }\n }\n\n async request<T>(path: string, opts: RequestOptions & { envId?: string } = {}): Promise<T> {\n if (Date.now() > this.creds.accessExpiresAt - 30_000) await this.refresh();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.creds.accessToken}`,\n \"X-Tenant-ID\": this.creds.tenantId,\n ...opts.headers,\n };\n if (opts.envId) headers[\"X-Environment-ID\"] = opts.envId;\n try {\n return await rawRequest<T>(this.creds.apiUrl, path, { ...opts, headers });\n } catch (err) {\n if (err instanceof ApiError && err.status === 401) {\n await this.refresh();\n headers.Authorization = `Bearer ${this.creds.accessToken}`;\n return rawRequest<T>(this.creds.apiUrl, path, { ...opts, headers });\n }\n throw err;\n }\n }\n}\n","import { parse } from \"yaml\";\n\nimport { CliError } from \"./output.js\";\n\n// userbox.yaml: permissions and roles as code.\n//\n// permissions: [projects.read, projects.create, projects.delete]\n// roles:\n// admin: [projects.*]\n// member: [projects.read, projects.create]\n//\n// A role entry ending in `.*` expands to every declared permission with that\n// prefix. `apply` makes each declared role hold exactly its listed\n// permissions; permissions and roles that exist only in the environment are\n// reported, never deleted.\n\nexport interface Manifest {\n permissions: string[];\n roles: Record<string, string[]>;\n}\n\n// Same rule as the API: 2 to 5 lowercase segments separated by dots.\nconst PERMISSION_KEY = /^[a-z][a-z0-9_-]*(\\.[a-z][a-z0-9_-]*){1,4}$/;\n\nexport function parseManifest(text: string): Manifest {\n let raw: unknown;\n try {\n raw = parse(text);\n } catch (err) {\n throw new CliError(\"MANIFEST_INVALID\", `userbox.yaml is not valid YAML: ${(err as Error).message}`);\n }\n const doc = (raw ?? {}) as { permissions?: unknown; roles?: unknown };\n const permissions = doc.permissions ?? [];\n if (!Array.isArray(permissions) || permissions.some((p) => typeof p !== \"string\")) {\n throw new CliError(\"MANIFEST_INVALID\", \"`permissions` must be a list of strings\");\n }\n for (const p of permissions as string[]) {\n if (!PERMISSION_KEY.test(p)) {\n throw new CliError(\n \"MANIFEST_INVALID\",\n `invalid permission key \"${p}\"`,\n \"Use 2 to 5 lowercase segments separated by dots, for example projects.delete.\",\n );\n }\n }\n const rolesRaw = doc.roles ?? {};\n if (typeof rolesRaw !== \"object\" || Array.isArray(rolesRaw)) {\n throw new CliError(\"MANIFEST_INVALID\", \"`roles` must map role names to lists of permissions\");\n }\n const declared = new Set(permissions as string[]);\n const roles: Record<string, string[]> = {};\n for (const [name, list] of Object.entries(rolesRaw as Record<string, unknown>)) {\n if (!Array.isArray(list) || list.some((p) => typeof p !== \"string\")) {\n throw new CliError(\"MANIFEST_INVALID\", `role \"${name}\" must be a list of permission keys`);\n }\n const expanded = new Set<string>();\n for (const entry of list as string[]) {\n if (entry.endsWith(\".*\")) {\n const prefix = entry.slice(0, -1);\n const matches = [...declared].filter((p) => p.startsWith(prefix));\n if (matches.length === 0) {\n throw new CliError(\"MANIFEST_INVALID\", `role \"${name}\": \"${entry}\" matches no declared permission`);\n }\n matches.forEach((m) => expanded.add(m));\n } else if (!declared.has(entry)) {\n throw new CliError(\n \"MANIFEST_INVALID\",\n `role \"${name}\" uses \"${entry}\", which is not under \\`permissions\\``,\n );\n } else {\n expanded.add(entry);\n }\n }\n roles[name] = [...expanded].sort();\n }\n return { permissions: [...declared].sort(), roles };\n}\n\nexport interface RemoteState {\n permissions: string[];\n /** Role name → permission keys it holds now. */\n roles: Record<string, string[]>;\n}\n\nexport interface Plan {\n createPermissions: string[];\n createRoles: string[];\n grant: { role: string; permission: string }[];\n revoke: { role: string; permission: string }[];\n /** Exist in the environment but not in the file. Reported only. */\n extraPermissions: string[];\n extraRoles: string[];\n}\n\nexport function planApply(manifest: Manifest, remote: RemoteState): Plan {\n const remotePerms = new Set(remote.permissions);\n const plan: Plan = {\n createPermissions: manifest.permissions.filter((p) => !remotePerms.has(p)),\n createRoles: Object.keys(manifest.roles).filter((r) => !(r in remote.roles)).sort(),\n grant: [],\n revoke: [],\n extraPermissions: remote.permissions.filter((p) => !manifest.permissions.includes(p)).sort(),\n extraRoles: Object.keys(remote.roles).filter((r) => !(r in manifest.roles)).sort(),\n };\n for (const [role, wanted] of Object.entries(manifest.roles)) {\n const have = new Set(remote.roles[role] ?? []);\n for (const p of wanted) if (!have.has(p)) plan.grant.push({ role, permission: p });\n for (const p of have) if (!wanted.includes(p)) plan.revoke.push({ role, permission: p });\n }\n return plan;\n}\n\nexport function planIsEmpty(plan: Plan): boolean {\n return (\n plan.createPermissions.length === 0 &&\n plan.createRoles.length === 0 &&\n plan.grant.length === 0 &&\n plan.revoke.length === 0\n );\n}\n","import { createInterface } from \"node:readline\";\nimport { Writable } from \"node:stream\";\n\nimport { CliError } from \"./output.js\";\n\n/**\n * Interactive input exists only for a human at a terminal. Commands that need\n * a secret (the password) refuse to run without a TTY, so a coding agent\n * cannot type one and never sees it.\n */\nexport function requireTty(command: string): void {\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n throw new CliError(\n \"NEEDS_HUMAN\",\n `\\`userbox ${command}\\` must be run by a human in an interactive terminal`,\n `Ask the human to run \\`npx @userbox/cli ${command}\\` in their own terminal, then continue.`,\n );\n }\n}\n\nexport async function ask(question: string, { secret = false, fallback = \"\" } = {}): Promise<string> {\n let muted = false;\n const output = new Writable({\n write(chunk, _enc, cb) {\n if (!muted) process.stdout.write(chunk);\n cb();\n },\n });\n const rl = createInterface({ input: process.stdin, output, terminal: true });\n const label = fallback ? `${question} (${fallback}): ` : `${question}: `;\n try {\n return await new Promise<string>((resolve) => {\n rl.question(label, (answer) => {\n if (secret) process.stdout.write(\"\\n\");\n resolve(answer.trim() || fallback);\n });\n muted = secret;\n });\n } finally {\n rl.close();\n }\n}\n\nexport async function confirm(question: string): Promise<boolean> {\n const answer = await ask(`${question} [y/N]`);\n return /^y(es)?$/i.test(answer);\n}\n","import type { Session } from \"./api.js\";\nimport { CliError } from \"./output.js\";\n\n// Thin typed calls to the management API, as a tenant admin.\n\nexport interface Environment {\n id: string;\n name: string;\n type: \"production\" | \"staging\" | \"development\";\n isDefault: boolean;\n}\n\nexport interface Workspace {\n id: string;\n name: string;\n slug: string | null;\n status: string;\n}\n\nexport interface Permission {\n id: string;\n key: string;\n type: \"internal\" | \"external\";\n}\n\nexport interface Role {\n id: string;\n name: string;\n}\n\nexport interface AuthSettings {\n allowed_redirect_domains: string[] | null;\n allow_self_signup: boolean;\n}\n\nexport async function defaultEnvironment(s: Session): Promise<Environment> {\n const { environments } = await s.request<{ environments: Environment[] }>(\"/v1/environments\");\n const env = environments.find((e) => e.isDefault);\n if (!env) throw new CliError(\"NO_DEFAULT_ENVIRONMENT\", \"the tenant has no default environment\");\n return env;\n}\n\nexport function getAuthSettings(s: Session, env: Environment): Promise<AuthSettings> {\n return s.request<AuthSettings>(`/v1/environments/${env.id}/auth-settings`);\n}\n\n/** Adds `host` (a bare hostname such as `localhost`) to the allowed redirect domains. */\nexport async function ensureRedirectDomain(s: Session, env: Environment, host: string): Promise<boolean> {\n const settings = await getAuthSettings(s, env);\n const domains = settings.allowed_redirect_domains ?? [];\n if (domains.includes(host)) return false;\n await s.request(`/v1/environments/${env.id}/auth-settings`, {\n method: \"PATCH\",\n body: { allowed_redirect_domains: [...domains, host] },\n });\n return true;\n}\n\nexport async function createApiKey(\n s: Session,\n env: Environment,\n name: string,\n scopes: string[],\n): Promise<string> {\n const res = await s.request<{ key: string }>(\"/v1/api-keys\", {\n method: \"POST\",\n envId: env.id,\n body: { name, scopes },\n });\n return res.key;\n}\n\nexport async function listWorkspaces(s: Session, env: Environment): Promise<Workspace[]> {\n const res = await s.request<{ items: Workspace[] }>(\"/v1/workspaces?status=active&per_page=100\", {\n envId: env.id,\n });\n return res.items;\n}\n\nexport function createWorkspace(s: Session, env: Environment, name: string, slug: string): Promise<Workspace> {\n return s.request<Workspace>(\"/v1/workspaces\", { method: \"POST\", envId: env.id, body: { name, slug } });\n}\n\nexport async function listExternalPermissions(s: Session, env: Environment): Promise<Permission[]> {\n const all: Permission[] = [];\n let cursor: string | null = null;\n do {\n const q: string = cursor ? `&cursor=${encodeURIComponent(cursor)}` : \"\";\n const page: { items: Permission[]; next_cursor: string | null } = await s.request(\n `/v1/permissions?type=external&limit=200${q}`,\n { envId: env.id },\n );\n all.push(...page.items);\n cursor = page.next_cursor;\n } while (cursor);\n return all;\n}\n\nexport function createPermission(s: Session, env: Environment, key: string): Promise<Permission> {\n return s.request<Permission>(\"/v1/permissions\", { method: \"POST\", envId: env.id, body: { key, name: key } });\n}\n\nexport async function listRoles(s: Session, env: Environment, workspaceId: string): Promise<Role[]> {\n const res = await s.request<{ items: Role[] }>(`/v1/workspaces/${workspaceId}/roles?limit=200`, {\n envId: env.id,\n });\n return res.items;\n}\n\nexport function createRole(s: Session, env: Environment, workspaceId: string, name: string): Promise<Role> {\n return s.request<Role>(`/v1/workspaces/${workspaceId}/roles`, { method: \"POST\", envId: env.id, body: { name } });\n}\n\nexport interface RolePermission {\n permission_id: string;\n key: string;\n type: \"internal\" | \"external\";\n}\n\nexport async function listRolePermissions(\n s: Session,\n env: Environment,\n workspaceId: string,\n roleId: string,\n): Promise<RolePermission[]> {\n const res = await s.request<{ items: RolePermission[] }>(\n `/v1/workspaces/${workspaceId}/roles/${roleId}/permissions?limit=200`,\n { envId: env.id },\n );\n return res.items;\n}\n\nexport interface Membership {\n id: string;\n user_id: string | null;\n workspace_id: string;\n email: string | null;\n status: string;\n}\n\nexport async function listMemberships(s: Session, workspaceId: string): Promise<Membership[]> {\n const res = await s.request<{ items: Membership[] }>(\n `/v1/memberships?workspace_id=${workspaceId}&per_page=100`,\n );\n return res.items;\n}\n\nexport function inviteMember(s: Session, workspaceId: string, email: string): Promise<Membership> {\n return s.request<Membership>(\"/v1/memberships\", { method: \"POST\", body: { email, workspace_id: workspaceId } });\n}\n\nexport function assignRole(s: Session, membershipId: string, roleId: string): Promise<unknown> {\n return s.request(`/v1/memberships/${membershipId}/roles`, { method: \"POST\", body: { role_id: roleId } });\n}\nexport function grantRolePermission(\n s: Session,\n env: Environment,\n workspaceId: string,\n roleId: string,\n permissionId: string,\n): Promise<unknown> {\n return s.request(`/v1/workspaces/${workspaceId}/roles/${roleId}/permissions`, {\n method: \"POST\",\n envId: env.id,\n body: { permission_id: permissionId, effect: \"allow\" },\n });\n}\n\nexport function revokeRolePermission(\n s: Session,\n env: Environment,\n workspaceId: string,\n roleId: string,\n permissionId: string,\n): Promise<unknown> {\n return s.request(`/v1/workspaces/${workspaceId}/roles/${roleId}/permissions/${permissionId}`, {\n method: \"DELETE\",\n envId: env.id,\n });\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { Session } from \"../api.js\";\nimport type { Flags } from \"../bin.js\";\nimport type { Output } from \"../output.js\";\nimport {\n assignRole,\n createWorkspace,\n defaultEnvironment,\n inviteMember,\n listMemberships,\n listRoles,\n listWorkspaces,\n} from \"../userbox.js\";\nimport { executeSync, loadManifest, syncManifest } from \"./apply.js\";\n\nconst SLUG = \"dev\";\n\n// A dev workspace where the logged-in admin holds the `admin` role, plus\n// optional invited members. The API has no \"create user with password\"\n// route, so other users join by invitation email.\n\nexport async function devSeed(flags: Flags, out: Output): Promise<void> {\n const session = Session.load();\n const env = await defaultEnvironment(session);\n // A new tenant's default environment has type \"production\"; on the alpha\n // platform that is still test data, so warn instead of refusing.\n if (env.type === \"production\") {\n out.warn(`The default environment \"${env.name}\" is a production environment; the dev workspace is created there.`);\n }\n\n const workspaces = await listWorkspaces(session, env);\n let ws = workspaces.find((w) => w.slug === SLUG);\n if (ws) out.step(`Workspace ${SLUG} exists`);\n else {\n ws = await createWorkspace(session, env, \"Dev workspace\", SLUG);\n out.step(`Created workspace ${SLUG}`);\n }\n\n if (existsSync(join(process.cwd(), \"userbox.yaml\"))) {\n const sync = await syncManifest(session, env, loadManifest(process.cwd()), [ws]);\n await executeSync(session, env, sync);\n out.step(\"Synced userbox.yaml roles into the workspace\");\n } else {\n out.warn(\"No userbox.yaml: the workspace has no roles yet. Add one and run `userbox apply`.\");\n }\n\n const roles = new Map((await listRoles(session, env, ws.id)).map((r) => [r.name, r.id]));\n const memberships = await listMemberships(session, ws.id);\n const mine = memberships.find((m) => m.user_id === session.userId);\n let adminAssigned = false;\n const adminRole = roles.get(\"admin\");\n if (mine && adminRole) {\n await assignRole(session, mine.id, adminRole);\n adminAssigned = true;\n out.step(`You (${session.email}) have the admin role in ${SLUG}`);\n } else if (!adminRole) {\n out.warn(\"No `admin` role in userbox.yaml, so your membership has no role.\");\n } else {\n out.warn(\"You have no membership in the dev workspace; it was created by another user.\");\n }\n\n const invited: { email: string; role: string | null }[] = [];\n for (const spec of flags.invite) {\n const [email, roleName = \"member\"] = spec.split(\":\");\n if (!email) continue;\n const already = memberships.find((m) => m.email?.toLowerCase() === email.toLowerCase());\n const membership = already ?? (await inviteMember(session, ws.id, email));\n const roleId = roles.get(roleName);\n if (roleId) await assignRole(session, membership.id, roleId);\n else out.warn(`No role \"${roleName}\" for ${email}; invited without a role.`);\n invited.push({ email, role: roleId ? roleName : null });\n out.step(already ? `${email} is already a member` : `Invited ${email} as ${roleName}`);\n }\n\n out.done(\n { workspace: { id: ws.id, slug: SLUG }, environment: { id: env.id, name: env.name }, adminAssigned, invited },\n [\n `Dev workspace: ${ws.id}`,\n \"Sign in to your app with your Userbox login; you are admin in this workspace.\",\n ...(flags.invite.length === 0\n ? [\"Add a second user with `userbox dev seed --invite someone@example.com:member` (they get an email).\"]\n : []),\n ],\n );\n}\n","import { randomUUID } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { ApiError, Session, rawRequest } from \"../api.js\";\nimport type { Flags } from \"../bin.js\";\nimport { loadCredentials } from \"../credentials.js\";\nimport { ENV_KEYS, parseEnv } from \"../envfile.js\";\nimport { CliError, type Output } from \"../output.js\";\nimport { detectNextProject } from \"../project.js\";\nimport { defaultEnvironment, getAuthSettings } from \"../userbox.js\";\n\ntype Status = \"pass\" | \"warn\" | \"fail\" | \"skip\";\n\ninterface Check {\n name: string;\n status: Status;\n message: string;\n hint?: string;\n}\n\nconst MIN = { \"@userbox/react\": 4, \"@userbox/node\": 1 } as const;\n\nfunction alphaNumber(version: string): number | null {\n const m = /^1\\.0\\.0-alpha\\.(\\d+)$/.exec(version);\n if (m) return Number(m[1]);\n return /^\\d+\\.\\d+\\.\\d+$/.test(version) && !version.startsWith(\"0.\") ? Number.POSITIVE_INFINITY : null;\n}\n\nexport async function doctor(flags: Flags, out: Output): Promise<number> {\n const root = process.cwd();\n const checks: Check[] = [];\n const add = (c: Check) => {\n checks.push(c);\n const mark = { pass: \"✓\", warn: \"!\", fail: \"✗\", skip: \"-\" }[c.status];\n out.step(`${mark} ${c.name}: ${c.message}${c.hint && c.status !== \"pass\" ? `\\n → ${c.hint}` : \"\"}`);\n };\n\n // Project and files\n let appDir = \"app\";\n try {\n const project = detectNextProject(root);\n appDir = project.appDir;\n add({ name: \"project\", status: \"pass\", message: `Next.js ${project.nextVersion ?? \"\"} (${appDir})`.trim() });\n } catch (err) {\n add({ name: \"project\", status: \"fail\", message: (err as Error).message, hint: (err as CliError).hint });\n }\n\n for (const [name, min] of Object.entries(MIN)) {\n const pkgPath = join(root, \"node_modules\", name, \"package.json\");\n if (!existsSync(pkgPath)) {\n add({ name, status: \"fail\", message: \"not installed\", hint: \"Run `userbox init` or install @userbox/react@alpha @userbox/node@alpha.\" });\n continue;\n }\n const version = (JSON.parse(readFileSync(pkgPath, \"utf8\")) as { version: string }).version;\n const n = alphaNumber(version);\n add(\n n !== null && n >= min\n ? { name, status: \"pass\", message: version }\n : { name, status: \"fail\", message: `${version} is too old`, hint: `Install ${name}@alpha (1.0.0-alpha.${min} or newer).` },\n );\n }\n\n const files = [\n `${appDir}/api/auth/refresh/route.ts`,\n `${appDir}/auth/callback/page.tsx`,\n ];\n for (const f of files) {\n const found = [f, f.replace(/\\.tsx?$/, \".js\"), f.replace(/\\.tsx$/, \".jsx\")].some((p) => existsSync(join(root, p)));\n add(found\n ? { name: f, status: \"pass\", message: \"present\" }\n : { name: f, status: \"fail\", message: \"missing\", hint: \"Run `userbox init` to write it.\" });\n }\n\n // Environment file\n const envPath = join(root, \".env.local\");\n const env = existsSync(envPath) ? parseEnv(readFileSync(envPath, \"utf8\")) : new Map<string, string>();\n const missing = ENV_KEYS.filter((k) => !env.get(k));\n add(\n missing.length === 0\n ? { name: \".env.local\", status: \"pass\", message: \"all five variables set\" }\n : { name: \".env.local\", status: \"fail\", message: `missing ${missing.join(\", \")}`, hint: \"Run `userbox init` (it never overwrites existing values without --force).\" },\n );\n const leaked = [...env.entries()].filter(\n ([k, v]) => k.startsWith(\"NEXT_PUBLIC_\") && (/KEY|SECRET|TOKEN|PASSWORD/.test(k) || /^ub_(live|prod|stg|dev)_/.test(v)),\n );\n add(\n leaked.length === 0\n ? { name: \"secrets\", status: \"pass\", message: \"no secret in a NEXT_PUBLIC_ variable\" }\n : { name: \"secrets\", status: \"fail\", message: `${leaked.map(([k]) => k).join(\", \")} is exposed to the browser`, hint: \"Rename it without NEXT_PUBLIC_ and rotate the key in the console.\" },\n );\n const gitignore = existsSync(join(root, \".gitignore\")) ? readFileSync(join(root, \".gitignore\"), \"utf8\") : \"\";\n add(\n /^(\\.env\\*|\\.env\\.local|\\.env\\*\\.local|\\*\\.local)\\s*$/m.test(gitignore)\n ? { name: \".gitignore\", status: \"pass\", message: \".env.local is ignored\" }\n : { name: \".gitignore\", status: \"fail\", message: \".env.local is not ignored\", hint: \"Add `.env.local` to .gitignore before committing.\" },\n );\n\n // Live checks against the API\n const apiUrl = env.get(\"NEXT_PUBLIC_USERBOX_API_URL\");\n const apiKey = env.get(\"USERBOX_API_KEY\");\n const tenantId = env.get(\"USERBOX_TENANT_ID\");\n if (apiUrl) {\n try {\n const jwks = await rawRequest<{ keys: unknown[] }>(apiUrl, \"/.well-known/jwks.json\");\n add({ name: \"api\", status: jwks.keys.length > 0 ? \"pass\" : \"fail\", message: `${apiUrl} reachable, JWKS has ${jwks.keys.length} key(s)` });\n } catch (err) {\n add({ name: \"api\", status: \"fail\", message: (err as Error).message, hint: \"Check NEXT_PUBLIC_USERBOX_API_URL.\" });\n }\n }\n if (apiUrl && apiKey) {\n const auth = { Authorization: `Bearer ${apiKey}` };\n try {\n const t = await rawRequest<{ id: string; slug: string }>(apiUrl, \"/v1/tenants/me\", { headers: auth });\n const slugOk = t.slug === env.get(\"NEXT_PUBLIC_USERBOX_TENANT_SLUG\");\n add(\n t.id === tenantId && slugOk\n ? { name: \"api key\", status: \"pass\", message: `valid for tenant ${t.slug}` }\n : { name: \"api key\", status: \"fail\", message: `the key belongs to tenant ${t.slug} (${t.id}), not the one in .env.local`, hint: \"Fix USERBOX_TENANT_ID / NEXT_PUBLIC_USERBOX_TENANT_SLUG, or run `userbox init --force`.\" },\n );\n } catch (err) {\n add({ name: \"api key\", status: \"fail\", message: (err as Error).message, hint: \"The key is revoked or wrong: run `userbox init --force` for a new one.\" });\n }\n try {\n await rawRequest(apiUrl, \"/v1/access/check\", {\n method: \"POST\",\n headers: auth,\n body: { user_id: randomUUID(), workspace_id: randomUUID(), permission_key: \"doctor.probe\" },\n });\n add({ name: \"scope access:check\", status: \"pass\", message: \"granted\" });\n } catch (err) {\n const scope = err instanceof ApiError && err.code === \"INSUFFICIENT_SCOPE\";\n add(scope\n ? { name: \"scope access:check\", status: \"fail\", message: \"missing: can() always returns false\", hint: \"Run `userbox init --force` for a key with the right scopes.\" }\n : { name: \"scope access:check\", status: \"pass\", message: \"granted\" });\n }\n }\n\n // Checks that need the CLI login\n const creds = loadCredentials();\n if (!creds) {\n add({ name: \"redirect domain\", status: \"skip\", message: \"not logged in\", hint: \"Run `userbox login` to also check the tenant settings.\" });\n } else {\n try {\n const session = Session.load();\n if (tenantId && session.tenantId !== tenantId) {\n add({ name: \"login\", status: \"warn\", message: `logged in to ${session.tenantSlug}, but .env.local points at another tenant` });\n } else {\n const envr = await defaultEnvironment(session);\n const settings = await getAuthSettings(session, envr);\n const host = new URL(env.get(\"APP_ORIGIN\") ?? \"http://localhost:3000\").hostname;\n const domains = settings.allowed_redirect_domains ?? [];\n add(domains.includes(host)\n ? { name: \"redirect domain\", status: \"pass\", message: `${host} allowed` }\n : { name: \"redirect domain\", status: \"fail\", message: `${host} is not an allowed redirect domain`, hint: \"Run `userbox init`, or add it in Console → Auth settings.\" });\n add(settings.allow_self_signup\n ? { name: \"sign-up\", status: \"pass\", message: \"self-service sign-up is on\" }\n : { name: \"sign-up\", status: \"warn\", message: \"sign-up is invitation-only\", hint: \"Fine for dev. Turn on self-service sign-up in the console if new users should register.\" });\n }\n } catch (err) {\n add({ name: \"login\", status: \"warn\", message: (err as Error).message, hint: (err as CliError).hint });\n }\n }\n\n const failed = checks.filter((c) => c.status === \"fail\").length;\n out.done({ healthy: failed === 0, checks }, [\n failed === 0 ? \"\\nAll checks passed.\" : `\\n${failed} check(s) failed.`,\n ]);\n return failed === 0 ? 0 : 1;\n}\n","// Minimal .env reader/merger. It never rewrites or reorders existing lines:\n// values already present win unless the caller forces a key, and new keys are\n// appended in a block at the end.\n\nexport const ENV_KEYS = [\n \"NEXT_PUBLIC_USERBOX_TENANT_SLUG\",\n \"NEXT_PUBLIC_USERBOX_API_URL\",\n \"USERBOX_TENANT_ID\",\n \"USERBOX_API_KEY\",\n \"APP_ORIGIN\",\n] as const;\n\nexport type EnvKey = (typeof ENV_KEYS)[number];\n\nconst LINE = /^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*)$/;\n\nexport function parseEnv(text: string): Map<string, string> {\n const out = new Map<string, string>();\n for (const raw of text.split(/\\r?\\n/)) {\n const m = LINE.exec(raw);\n if (!m) continue;\n let value = (m[2] ?? \"\").trim();\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1);\n }\n out.set(m[1]!, value);\n }\n return out;\n}\n\nexport interface MergeResult {\n text: string;\n added: string[];\n replaced: string[];\n kept: string[];\n}\n\n/**\n * Merge `values` into an existing .env text. A key that already has a\n * non-empty value is kept, unless it is listed in `force`; then its line is\n * replaced in place. Missing or empty keys are written.\n */\nexport function mergeEnv(\n existing: string,\n values: Record<string, string>,\n force: ReadonlySet<string> = new Set(),\n): MergeResult {\n const current = parseEnv(existing);\n const added: string[] = [];\n const replaced: string[] = [];\n const kept: string[] = [];\n const lines = existing === \"\" ? [] : existing.replace(/\\n$/, \"\").split(/\\r?\\n/);\n const toAppend: string[] = [];\n\n for (const [key, value] of Object.entries(values)) {\n const had = current.get(key);\n if (had && !force.has(key)) {\n kept.push(key);\n continue;\n }\n const idx = lines.findIndex((l) => LINE.exec(l)?.[1] === key);\n if (idx >= 0) {\n lines[idx] = `${key}=${value}`;\n (had ? replaced : added).push(key);\n } else {\n toAppend.push(`${key}=${value}`);\n added.push(key);\n }\n }\n\n if (toAppend.length > 0) {\n if (lines.length > 0 && lines[lines.length - 1] !== \"\") lines.push(\"\");\n lines.push(\"# Userbox (written by `userbox init`)\", ...toAppend);\n }\n return { text: lines.length ? `${lines.join(\"\\n\")}\\n` : \"\", added, replaced, kept };\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { CliError } from \"./output.js\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nexport interface NextProject {\n root: string;\n /** \"app\" or \"src/app\". */\n appDir: string;\n packageManager: PackageManager;\n nextVersion: string | null;\n typescript: boolean;\n}\n\nexport function detectPackageManager(root: string): PackageManager {\n if (existsSync(join(root, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(root, \"yarn.lock\"))) return \"yarn\";\n if (existsSync(join(root, \"bun.lockb\")) || existsSync(join(root, \"bun.lock\"))) return \"bun\";\n return \"npm\";\n}\n\nexport function detectNextProject(root: string): NextProject {\n const pkgPath = join(root, \"package.json\");\n if (!existsSync(pkgPath)) {\n throw new CliError(\"NOT_A_PROJECT\", `no package.json in ${root}`, \"Run this in the root of your Next.js app.\");\n }\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (!deps.next) {\n throw new CliError(\n \"UNSUPPORTED_FRAMEWORK\",\n \"this is not a Next.js app (no `next` dependency)\",\n \"`userbox init` supports Next.js App Router only for now; see https://docs.test.userbox.app/llms.txt for other stacks.\",\n );\n }\n const appDir = existsSync(join(root, \"src\", \"app\")) ? \"src/app\" : existsSync(join(root, \"app\")) ? \"app\" : null;\n if (!appDir) {\n throw new CliError(\n \"UNSUPPORTED_FRAMEWORK\",\n \"no `app/` or `src/app/` directory: only the App Router is supported\",\n );\n }\n return {\n root,\n appDir,\n packageManager: detectPackageManager(root),\n nextVersion: deps.next ?? null,\n typescript: existsSync(join(root, \"tsconfig.json\")),\n };\n}\n\nexport function installCommand(pm: PackageManager, packages: string[]): [string, string[]] {\n switch (pm) {\n case \"pnpm\":\n return [\"pnpm\", [\"add\", ...packages]];\n case \"yarn\":\n return [\"yarn\", [\"add\", ...packages]];\n case \"bun\":\n return [\"bun\", [\"add\", ...packages]];\n default:\n return [\"npm\", [\"install\", ...packages]];\n }\n}\n","import { spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { Session } from \"../api.js\";\nimport type { Flags } from \"../bin.js\";\nimport { ENV_KEYS, mergeEnv, parseEnv } from \"../envfile.js\";\nimport { CliError, type Output } from \"../output.js\";\nimport { detectNextProject, installCommand } from \"../project.js\";\nimport { scaffold } from \"../scaffold.js\";\nimport { createApiKey, defaultEnvironment, ensureRedirectDomain } from \"../userbox.js\";\n\nexport const KEY_SCOPES = [\"access:check\", \"memberships:read\", \"workspaces:read\"];\nconst SDKS = [\"@userbox/react@alpha\", \"@userbox/node@alpha\"];\n\nfunction ensureGitignored(root: string): boolean {\n const path = join(root, \".gitignore\");\n const text = existsSync(path) ? readFileSync(path, \"utf8\") : \"\";\n const covered = text\n .split(/\\r?\\n/)\n .map((l) => l.trim())\n .some((l) => [\".env*\", \".env.local\", \".env*.local\", \"*.local\"].includes(l));\n if (covered) return false;\n writeFileSync(path, `${text}${text && !text.endsWith(\"\\n\") ? \"\\n\" : \"\"}.env.local\\n`);\n return true;\n}\n\nexport async function init(flags: Flags, out: Output): Promise<void> {\n const root = process.cwd();\n const project = detectNextProject(root);\n const session = Session.load();\n const origin = flags.origin ?? \"http://localhost:3000\";\n let host: string;\n try {\n host = new URL(origin).hostname;\n } catch {\n throw new CliError(\"INVALID_ORIGIN\", `--origin must be a URL such as http://localhost:3000, got \"${origin}\"`);\n }\n\n const env = await defaultEnvironment(session);\n out.step(`Tenant ${session.tenantSlug}, environment ${env.name} (${env.type})`);\n if (env.type === \"production\") out.warn(\"The default environment is production; the key created here is a production key.\");\n\n if (await ensureRedirectDomain(session, env, host)) out.step(`Added ${host} to the allowed redirect domains`);\n else out.step(`${host} is already an allowed redirect domain`);\n\n // .env.local: reuse an existing key unless --force; never print values.\n const envPath = join(root, \".env.local\");\n const existing = existsSync(envPath) ? readFileSync(envPath, \"utf8\") : \"\";\n const current = parseEnv(existing);\n const values: Record<string, string> = {\n NEXT_PUBLIC_USERBOX_TENANT_SLUG: session.tenantSlug,\n NEXT_PUBLIC_USERBOX_API_URL: session.apiUrl,\n USERBOX_TENANT_ID: session.tenantId,\n APP_ORIGIN: origin,\n };\n let keyCreated = false;\n if (flags.force || !current.get(\"USERBOX_API_KEY\")) {\n const date = new Date().toISOString().slice(0, 10);\n values.USERBOX_API_KEY = await createApiKey(session, env, `cli ${hostname()} ${date}`, KEY_SCOPES);\n keyCreated = true;\n out.step(`Created an API key with scopes ${KEY_SCOPES.join(\", \")}`);\n }\n const merged = mergeEnv(existing, values, flags.force ? new Set(ENV_KEYS) : new Set());\n writeFileSync(envPath, merged.text, { mode: 0o600 });\n out.step(`.env.local: ${merged.added.length} added, ${merged.replaced.length} replaced, ${merged.kept.length} kept`);\n const ignored = ensureGitignored(root);\n if (ignored) out.step(\"Added .env.local to .gitignore\");\n for (const key of merged.kept) {\n if (current.get(key) !== values[key] && key !== \"USERBOX_API_KEY\") {\n out.warn(`.env.local keeps a different ${key}; run with --force to replace it.`);\n }\n }\n\n let installed = false;\n if (!flags.skipInstall) {\n const [cmd, args] = installCommand(project.packageManager, SDKS);\n out.step(`Installing the SDKs: ${cmd} ${args.join(\" \")}`);\n const res = spawnSync(cmd, args, {\n cwd: root,\n stdio: flags.json ? [\"ignore\", \"ignore\", \"inherit\"] : \"inherit\",\n shell: process.platform === \"win32\",\n });\n if (res.status !== 0) {\n throw new CliError(\"INSTALL_FAILED\", `${cmd} exited with ${res.status ?? res.signal}`, `Run \\`${cmd} ${args.join(\" \")}\\` yourself.`);\n }\n installed = true;\n }\n\n const files = scaffold(project);\n for (const f of files.written) out.step(`Wrote ${f}`);\n for (const t of files.todo) out.warn(t);\n\n out.done(\n {\n tenant: { id: session.tenantId, slug: session.tenantSlug },\n environment: { id: env.id, name: env.name, type: env.type },\n redirectDomain: host,\n envFile: { path: \".env.local\", added: merged.added, replaced: merged.replaced, kept: merged.kept },\n apiKeyCreated: keyCreated,\n sdksInstalled: installed,\n files,\n next: [\"userbox apply\", \"userbox dev seed\", \"userbox doctor\"],\n },\n [\n \"\",\n \"Userbox is set up. Next:\",\n \" 1. Declare permissions and roles in userbox.yaml, then run `userbox apply`.\",\n \" 2. Run `userbox dev seed` for a workspace where you are admin.\",\n \" 3. Run `userbox doctor`, then start the app and sign in.\",\n \"Use verifyRequest() and can() from @userbox/node in your API routes (see https://docs.test.userbox.app/llms.txt).\",\n ],\n );\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nimport type { NextProject } from \"./project.js\";\n\n// The auth glue from llms.txt, written only where no file exists yet. Existing\n// files are never overwritten; the layout is edited only when the change is\n// unambiguous.\n\nexport interface ScaffoldResult {\n written: string[];\n skipped: string[];\n /** Things the CLI could not do safely; the human or agent does them. */\n todo: string[];\n}\n\nfunction providers(ts: boolean): string {\n const props = ts ? \"{ children }: { children: React.ReactNode }\" : \"{ children }\";\n const bang = ts ? \"!\" : \"\";\n return `\"use client\";\nimport { UserboxProvider } from \"@userbox/react\";\n\nexport function Providers(${props}) {\n return (\n <UserboxProvider\n tenantSlug={process.env.NEXT_PUBLIC_USERBOX_TENANT_SLUG${bang}}\n baseUrl={process.env.NEXT_PUBLIC_USERBOX_API_URL${bang}}\n tokenStorage=\"cookie\"\n refreshUrl=\"/api/auth/refresh\"\n >\n {children}\n </UserboxProvider>\n );\n}\n`;\n}\n\nconst CALLBACK = `\"use client\";\nimport { useRouter } from \"next/navigation\";\nimport { UserboxCallback } from \"@userbox/react\";\n\nexport default function Callback() {\n const router = useRouter();\n return <UserboxCallback onSuccess={() => router.replace(\"/\")} onError={(e) => console.error(e.message)} />;\n}\n`;\n\nconst REFRESH = `import { createRefreshHandler } from \"@userbox/react/nextjs\";\n\nexport const { POST } = createRefreshHandler();\n`;\n\nfunction writeIfMissing(root: string, rel: string, content: string, result: ScaffoldResult): void {\n const path = join(root, rel);\n if (existsSync(path)) {\n result.skipped.push(rel);\n return;\n }\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, content);\n result.written.push(rel);\n}\n\n/**\n * Wrap `{children}` in the root layout with `<Providers>`. Returns the new\n * text, or null when the layout already uses Providers or the edit is not\n * obvious (zero or several `{children}`).\n */\nexport function wrapLayout(text: string, importPath: string): string | null {\n if (/<Providers[\\s>]/.test(text)) return null;\n const occurrences = text.split(\"{children}\").length - 1;\n if (occurrences !== 1) return null;\n const wrapped = text.replace(\"{children}\", \"<Providers>{children}</Providers>\");\n const importLine = `import { Providers } from \"${importPath}\";\\n`;\n const lastImport = [...wrapped.matchAll(/^import .*$/gm)].pop();\n if (!lastImport || lastImport.index === undefined) return importLine + wrapped;\n const at = lastImport.index + lastImport[0].length;\n return `${wrapped.slice(0, at)}\\n${importLine.trimEnd()}${wrapped.slice(at)}`;\n}\n\nexport function scaffold(project: NextProject): ScaffoldResult {\n const result: ScaffoldResult = { written: [], skipped: [], todo: [] };\n const { root, appDir, typescript: ts } = project;\n const x = ts ? \"tsx\" : \"jsx\";\n const s = ts ? \"ts\" : \"js\";\n\n writeIfMissing(root, `${appDir}/providers.${x}`, providers(ts), result);\n writeIfMissing(root, `${appDir}/auth/callback/page.${x}`, CALLBACK, result);\n writeIfMissing(root, `${appDir}/api/auth/refresh/route.${s}`, REFRESH, result);\n\n const layoutRel = [`${appDir}/layout.tsx`, `${appDir}/layout.jsx`, `${appDir}/layout.js`].find((p) =>\n existsSync(join(root, p)),\n );\n if (!layoutRel) {\n result.todo.push(`Wrap {children} in your root layout with <Providers> from ./providers.`);\n return result;\n }\n const layoutPath = join(root, layoutRel);\n const layout = readFileSync(layoutPath, \"utf8\");\n if (/<Providers[\\s>]/.test(layout)) {\n result.skipped.push(layoutRel);\n return result;\n }\n const next = wrapLayout(layout, \"./providers\");\n if (next === null) {\n result.todo.push(`Wrap {children} in ${layoutRel} with <Providers> (import { Providers } from \"./providers\").`);\n } else {\n writeFileSync(layoutPath, next);\n result.written.push(`${layoutRel} (wrapped {children} with <Providers>)`);\n }\n return result;\n}\n","import { DEFAULT_API_URL, Session, expiryFrom, rawRequest, type TokenPair } from \"../api.js\";\nimport type { Flags } from \"../bin.js\";\nimport { clearCredentials, loadCredentials, saveCredentials } from \"../credentials.js\";\nimport type { Output } from \"../output.js\";\nimport { ask, requireTty } from \"../prompt.js\";\n\ninterface Tenant {\n id: string;\n slug: string;\n name: string;\n}\n\nexport async function login(flags: Flags, out: Output): Promise<void> {\n requireTty(\"login\");\n const previous = loadCredentials();\n const apiUrl = (flags.apiUrl ?? previous?.apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, \"\");\n const tenantSlug = await ask(\"Tenant slug\", { fallback: previous?.tenantSlug ?? \"\" });\n const email = await ask(\"Email\", { fallback: previous?.email ?? \"\" });\n const password = await ask(\"Password\", { secret: true });\n\n const tokens = await rawRequest<TokenPair>(apiUrl, \"/v1/auth/login\", {\n method: \"POST\",\n body: { email, password, tenant_slug: tenantSlug },\n });\n const tenant = await rawRequest<Tenant>(apiUrl, \"/v1/tenants/me\", {\n headers: { Authorization: `Bearer ${tokens.access_token}` },\n });\n saveCredentials({\n apiUrl,\n tenantSlug: tenant.slug,\n tenantId: tenant.id,\n email,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n accessExpiresAt: expiryFrom(tokens),\n });\n out.done({ tenant: { id: tenant.id, slug: tenant.slug }, email, apiUrl }, [\n `Logged in to ${tenant.name} (${tenant.slug}) as ${email}.`,\n \"The login is stored in ~/.config/userbox/credentials.json and lasts 7 days of inactivity.\",\n ]);\n}\n\nexport function logout(out: Output): void {\n clearCredentials();\n out.done({}, [\"Logged out.\"]);\n}\n\nexport async function whoami(out: Output): Promise<void> {\n const session = Session.load();\n const tenant = await session.request<Tenant>(\"/v1/tenants/me\");\n out.done(\n { tenant: { id: tenant.id, slug: tenant.slug }, email: session.email, apiUrl: session.apiUrl },\n [`${session.email} on ${tenant.name} (${tenant.slug}), ${session.apiUrl}`],\n );\n}\n"],"mappings":"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","names":["readFileSync","join","join","readFileSync","existsSync","join","existsSync","join","existsSync","readFileSync","join","existsSync","readFileSync","join","existsSync","join","readFileSync","join","existsSync","readFileSync","existsSync","readFileSync","writeFileSync","join","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","writeFileSync"]}
1
+ {"version":3,"sources":["../src/bin.ts","../src/commands/apply.ts","../src/credentials.ts","../src/output.ts","../src/api.ts","../src/manifest.ts","../src/prompt.ts","../src/userbox.ts","../src/commands/dev-seed.ts","../src/commands/doctor.ts","../src/envfile.ts","../src/project.ts","../src/commands/init.ts","../src/scaffold.ts","../src/commands/login.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\n\nimport { apply } from \"./commands/apply.js\";\nimport { devSeed } from \"./commands/dev-seed.js\";\nimport { doctor } from \"./commands/doctor.js\";\nimport { init } from \"./commands/init.js\";\nimport { login, logout, whoami } from \"./commands/login.js\";\nimport { CliError, createOutput, printError } from \"./output.js\";\n\nconst VERSION = \"0.1.0-alpha.1\";\n\nconst HELP = `userbox — set up Userbox in your app\n\nUsage: userbox <command> [options]\n\nCommands\n login Log in as a tenant admin (interactive, human only)\n logout Forget the local login\n whoami Show the current login\n init Set up this Next.js app: env, API key, SDKs, auth files\n apply Sync userbox.yaml (permissions and roles) to the environment\n dev seed Create a dev workspace with an admin and a member user\n doctor Check this app's Userbox setup\n\nOptions\n --json Machine-readable output (for coding agents)\n --yes, -y Apply without asking (apply)\n --force init: replace existing USERBOX_* values in .env.local\n --origin <url> init: the app origin (default http://localhost:3000)\n --skip-install init: do not install the SDKs\n --api-url <url> login: the Userbox API (default https://api.test.userbox.app)\n --invite <email[:role]> dev seed: invite a user (repeatable, role defaults to member)\n --help, -h Show this help\n --version, -v Show the version\n\nDocs: https://docs.test.userbox.app/llms.txt`;\n\nexport interface Flags {\n json: boolean;\n yes: boolean;\n force: boolean;\n origin?: string;\n skipInstall: boolean;\n apiUrl?: string;\n invite: string[];\n}\n\nasync function main(argv: string[]): Promise<number> {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n strict: true,\n options: {\n json: { type: \"boolean\", default: false },\n yes: { type: \"boolean\", short: \"y\", default: false },\n force: { type: \"boolean\", default: false },\n origin: { type: \"string\" },\n \"skip-install\": { type: \"boolean\", default: false },\n \"api-url\": { type: \"string\" },\n invite: { type: \"string\", multiple: true, default: [] },\n help: { type: \"boolean\", short: \"h\", default: false },\n version: { type: \"boolean\", short: \"v\", default: false },\n },\n });\n const flags: Flags = {\n json: values.json,\n yes: values.yes,\n force: values.force,\n origin: values.origin,\n skipInstall: values[\"skip-install\"],\n apiUrl: values[\"api-url\"],\n invite: values.invite,\n };\n if (values.version) {\n process.stdout.write(`${VERSION}\\n`);\n return 0;\n }\n const command = positionals.join(\" \");\n if (values.help || command === \"\") {\n process.stdout.write(`${HELP}\\n`);\n return 0;\n }\n const out = createOutput(flags.json);\n try {\n switch (command) {\n case \"login\":\n await login(flags, out);\n break;\n case \"logout\":\n logout(out);\n break;\n case \"whoami\":\n await whoami(out);\n break;\n case \"init\":\n await init(flags, out);\n break;\n case \"apply\":\n await apply(flags, out);\n break;\n case \"dev seed\":\n await devSeed(flags, out);\n break;\n case \"doctor\":\n return await doctor(flags, out);\n default:\n throw new CliError(\"UNKNOWN_COMMAND\", `unknown command \"${command}\"`, \"Run `userbox --help`.\");\n }\n return 0;\n } catch (err) {\n printError(err, flags.json);\n return 1;\n }\n}\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (err) => {\n printError(err, process.argv.includes(\"--json\"));\n process.exit(1);\n },\n);\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { Session } from \"../api.js\";\nimport type { Flags } from \"../bin.js\";\nimport { type Manifest, type Plan, parseManifest, planApply, planIsEmpty, planRevokes } from \"../manifest.js\";\nimport { CliError, type Output } from \"../output.js\";\nimport { confirm } from \"../prompt.js\";\nimport {\n type Environment,\n type Workspace,\n createPermission,\n createRole,\n defaultEnvironment,\n grantRolePermission,\n listExternalPermissions,\n listRolePermissions,\n listRoles,\n listWorkspaces,\n revokeRolePermission,\n} from \"../userbox.js\";\n\n// Permissions live in the environment; roles live in each workspace. `apply`\n// creates the permissions once and syncs the roles into every active\n// workspace of the default environment.\n\ntype Sync = Awaited<ReturnType<typeof syncManifest>>;\n\nfunction isInteractive(flags: Flags): boolean {\n return Boolean(process.stdin.isTTY && process.stdout.isTTY) && !flags.json;\n}\n\n/**\n * A plan that removes permissions from an existing role can lock real users\n * out, and coding agents skip the plan review (agent experiment, batch 4).\n * Such a plan always needs a human answer at a terminal: `--yes` does not\n * skip it, and without a terminal the command stops with NEEDS_HUMAN.\n */\nexport async function confirmRevokes(sync: Sync, flags: Flags, command: string): Promise<boolean> {\n if (!sync.plans.some((p) => planRevokes(p.plan))) return true;\n const lines = describe(sync).filter((l) => l.startsWith(\" - \"));\n if (!isInteractive(flags)) {\n throw new CliError(\n \"NEEDS_HUMAN\",\n `the plan removes permissions from existing roles:\\n${lines.join(\"\\n\")}`,\n `Ask the human to review it and run \\`npx @userbox/cli ${command}\\` in their own terminal.`,\n );\n }\n process.stdout.write(`This removes permissions from existing roles:\\n${lines.join(\"\\n\")}\\n`);\n return confirm(\"Remove them?\");\n}\n\nexport function loadManifest(root: string): Manifest {\n const path = join(root, \"userbox.yaml\");\n if (!existsSync(path)) {\n throw new CliError(\n \"NO_MANIFEST\",\n \"no userbox.yaml in this directory\",\n \"Create one:\\n permissions: [projects.read, projects.delete]\\n roles:\\n admin: [projects.*]\\n member: [projects.read]\",\n );\n }\n return parseManifest(readFileSync(path, \"utf8\"));\n}\n\ninterface WorkspacePlan {\n workspace: Workspace;\n plan: Plan;\n roleIds: Map<string, string>;\n}\n\nasync function workspacePlan(s: Session, env: Environment, manifest: Manifest, ws: Workspace, envPerms: string[]) {\n const roles = await listRoles(s, env, ws.id);\n const remoteRoles: Record<string, string[]> = {};\n const roleIds = new Map<string, string>();\n for (const role of roles) {\n roleIds.set(role.name, role.id);\n const perms = await listRolePermissions(s, env, ws.id, role.id);\n remoteRoles[role.name] = perms.filter((p) => p.type === \"external\").map((p) => p.key);\n }\n return { workspace: ws, plan: planApply(manifest, { permissions: envPerms, roles: remoteRoles }), roleIds };\n}\n\n/** Make the workspace's roles match the manifest. Permissions must already exist. */\nexport async function executeRoles(\n s: Session,\n env: Environment,\n wp: WorkspacePlan,\n permissionIds: Map<string, string>,\n): Promise<void> {\n const ids = new Map(wp.roleIds);\n for (const name of wp.plan.createRoles) {\n ids.set(name, (await createRole(s, env, wp.workspace.id, name)).id);\n }\n for (const { role, permission } of wp.plan.grant) {\n await grantRolePermission(s, env, wp.workspace.id, ids.get(role)!, permissionIds.get(permission)!);\n }\n for (const { role, permission } of wp.plan.revoke) {\n const pid = permissionIds.get(permission);\n if (pid) await revokeRolePermission(s, env, wp.workspace.id, ids.get(role)!, pid);\n }\n}\n\nexport async function syncManifest(\n s: Session,\n env: Environment,\n manifest: Manifest,\n workspaces: Workspace[],\n): Promise<{ createdPermissions: string[]; plans: WorkspacePlan[]; permissionIds: Map<string, string> }> {\n const existing = await listExternalPermissions(s, env);\n const permissionIds = new Map(existing.map((p) => [p.key, p.id]));\n const plans: WorkspacePlan[] = [];\n for (const ws of workspaces) plans.push(await workspacePlan(s, env, manifest, ws, [...permissionIds.keys()]));\n return { createdPermissions: manifest.permissions.filter((p) => !permissionIds.has(p)), plans, permissionIds };\n}\n\nexport async function executeSync(\n s: Session,\n env: Environment,\n sync: Awaited<ReturnType<typeof syncManifest>>,\n): Promise<void> {\n for (const key of sync.createdPermissions) {\n sync.permissionIds.set(key, (await createPermission(s, env, key)).id);\n }\n for (const wp of sync.plans) await executeRoles(s, env, wp, sync.permissionIds);\n}\n\nfunction describe(sync: Sync): string[] {\n const lines: string[] = [];\n for (const p of sync.createdPermissions) lines.push(` + permission ${p}`);\n for (const { workspace, plan } of sync.plans) {\n const label = workspace.slug ?? workspace.name;\n for (const r of plan.createRoles) lines.push(` + role ${r} in ${label}`);\n for (const g of plan.grant) lines.push(` + ${g.role} → ${g.permission} in ${label}`);\n for (const r of plan.revoke) lines.push(` - ${r.role} → ${r.permission} in ${label}`);\n }\n return lines;\n}\n\nexport async function apply(flags: Flags, out: Output): Promise<void> {\n const manifest = loadManifest(process.cwd());\n const session = Session.load();\n const env = await defaultEnvironment(session);\n const workspaces = await listWorkspaces(session, env);\n if (workspaces.length === 0) {\n out.warn(\"No workspaces yet: roles are created per workspace. Run `userbox dev seed` to create one.\");\n }\n const sync = await syncManifest(session, env, manifest, workspaces);\n const changes = describe(sync);\n const extras = new Set(sync.plans.flatMap((p) => p.plan.extraPermissions));\n if (extras.size > 0) out.warn(`Not in userbox.yaml, left as they are: ${[...extras].join(\", \")}`);\n const empty = sync.createdPermissions.length === 0 && sync.plans.every((p) => planIsEmpty(p.plan));\n const summary = {\n environment: { id: env.id, name: env.name },\n workspaces: workspaces.map((w) => w.slug ?? w.id),\n changes,\n };\n if (empty) {\n out.done({ ...summary, applied: false, upToDate: true }, [\"Up to date: nothing to apply.\"]);\n return;\n }\n if (!flags.yes) {\n if (!isInteractive(flags)) {\n out.done({ ...summary, applied: false, hint: \"Re-run with --yes to apply.\" }, [\n \"Plan:\",\n ...changes,\n \"Re-run with --yes to apply.\",\n ]);\n return;\n }\n process.stdout.write(`Plan for ${env.name}:\\n${changes.join(\"\\n\")}\\n`);\n if (!(await confirm(\"Apply these changes?\"))) {\n out.done({ ...summary, applied: false }, [\"Nothing applied.\"]);\n return;\n }\n } else if (!(await confirmRevokes(sync, flags, \"apply\"))) {\n out.done({ ...summary, applied: false }, [\"Nothing applied.\"]);\n return;\n }\n await executeSync(session, env, sync);\n out.done({ ...summary, applied: true }, [\"Applied:\", ...changes]);\n}\n","import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n// The CLI login is a normal tenant-admin session: a 15-minute access token and\n// a single-use refresh token, rotated on every refresh. Both live in one file,\n// mode 600, in a mode-700 directory. Nothing else is ever written there.\n\nexport interface Credentials {\n apiUrl: string;\n tenantSlug: string;\n tenantId: string;\n email: string;\n accessToken: string;\n refreshToken: string;\n /** Epoch milliseconds. */\n accessExpiresAt: number;\n}\n\nexport function configDir(): string {\n const base = process.env.XDG_CONFIG_HOME || join(homedir(), \".config\");\n return join(base, \"userbox\");\n}\n\nfunction credentialsPath(): string {\n return join(configDir(), \"credentials.json\");\n}\n\nexport function loadCredentials(): Credentials | null {\n try {\n return JSON.parse(readFileSync(credentialsPath(), \"utf8\")) as Credentials;\n } catch {\n return null;\n }\n}\n\nexport function saveCredentials(creds: Credentials): void {\n const dir = configDir();\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n chmodSync(dir, 0o700);\n const path = credentialsPath();\n writeFileSync(path, `${JSON.stringify(creds, null, 2)}\\n`, { mode: 0o600 });\n chmodSync(path, 0o600);\n}\n\nexport function clearCredentials(): void {\n rmSync(credentialsPath(), { force: true });\n}\n","// Every command prints for two readers: a human, or (with --json) a coding\n// agent. Errors always carry a stable code and a hint that says what to do.\n\nexport class CliError extends Error {\n constructor(\n readonly code: string,\n message: string,\n readonly hint?: string,\n ) {\n super(message);\n }\n}\n\nexport interface Output {\n json: boolean;\n /** A progress line for humans; silent in --json mode. */\n step(message: string): void;\n /** A warning; kept for the JSON result in --json mode. */\n warn(message: string): void;\n /** The final result: a JSON document, or the human summary lines. */\n done(result: Record<string, unknown>, lines: string[]): void;\n}\n\nexport function createOutput(json: boolean): Output {\n const warnings: string[] = [];\n return {\n json,\n step(message) {\n if (!json) process.stdout.write(` ${message}\\n`);\n },\n warn(message) {\n warnings.push(message);\n if (!json) process.stderr.write(` ! ${message}\\n`);\n },\n done(result, lines) {\n if (json) {\n process.stdout.write(`${JSON.stringify({ ok: true, ...result, warnings }, null, 2)}\\n`);\n } else {\n for (const line of lines) process.stdout.write(`${line}\\n`);\n }\n },\n };\n}\n\nexport function printError(err: unknown, json: boolean): void {\n const e =\n err instanceof CliError\n ? err\n : new CliError(\"UNEXPECTED\", err instanceof Error ? err.message : String(err));\n if (json) {\n process.stdout.write(\n `${JSON.stringify({ ok: false, error: { code: e.code, message: e.message, hint: e.hint ?? null } }, null, 2)}\\n`,\n );\n } else {\n process.stderr.write(`error [${e.code}]: ${e.message}\\n`);\n if (e.hint) process.stderr.write(`hint: ${e.hint}\\n`);\n }\n}\n","import { type Credentials, loadCredentials, saveCredentials } from \"./credentials.js\";\nimport { CliError } from \"./output.js\";\n\nexport const DEFAULT_API_URL = \"https://api.test.userbox.app\";\n\ninterface ApiErrorBody {\n code?: string;\n error?: string;\n detail?: unknown;\n}\n\nexport class ApiError extends CliError {\n constructor(\n readonly status: number,\n code: string,\n message: string,\n ) {\n super(code, `${message} (HTTP ${status})`);\n }\n}\n\nasync function parseError(res: Response): Promise<ApiError> {\n let body: ApiErrorBody = {};\n try {\n body = (await res.json()) as ApiErrorBody;\n } catch {\n // non-JSON error body\n }\n const message =\n body.error ??\n (typeof body.detail === \"string\" ? body.detail : body.detail ? JSON.stringify(body.detail) : res.statusText);\n return new ApiError(res.status, body.code ?? `HTTP_${res.status}`, message);\n}\n\nexport interface RequestOptions {\n method?: string;\n body?: unknown;\n headers?: Record<string, string>;\n}\n\nexport async function rawRequest<T>(apiUrl: string, path: string, opts: RequestOptions = {}): Promise<T> {\n let res: Response;\n try {\n res = await fetch(`${apiUrl.replace(/\\/$/, \"\")}${path}`, {\n method: opts.method ?? \"GET\",\n headers: {\n Accept: \"application/json\",\n ...(opts.body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n ...opts.headers,\n },\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n });\n } catch (err) {\n throw new CliError(\"NETWORK\", `cannot reach ${apiUrl}: ${(err as Error).message}`);\n }\n if (!res.ok) throw await parseError(res);\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n}\n\nexport interface TokenPair {\n access_token: string;\n refresh_token: string;\n expires_in?: number;\n}\n\nexport function expiryFrom(tokens: TokenPair): number {\n return Date.now() + (tokens.expires_in ?? 900) * 1000;\n}\n\n/** An authenticated client bound to the stored CLI login. */\nexport class Session {\n private constructor(private creds: Credentials) {}\n\n static load(): Session {\n const creds = loadCredentials();\n if (!creds) {\n throw new CliError(\n \"NOT_LOGGED_IN\",\n \"not logged in\",\n \"Ask the human to run `npx @userbox/cli login` in their own terminal, then retry.\",\n );\n }\n return new Session(creds);\n }\n\n get apiUrl(): string {\n return this.creds.apiUrl;\n }\n get tenantSlug(): string {\n return this.creds.tenantSlug;\n }\n get tenantId(): string {\n return this.creds.tenantId;\n }\n get email(): string {\n return this.creds.email;\n }\n /** The logged-in user's id: the `sub` of our own access token (not an authorization decision). */\n get userId(): string {\n const payload = this.creds.accessToken.split(\".\")[1] ?? \"\";\n return (JSON.parse(Buffer.from(payload, \"base64url\").toString(\"utf8\")) as { sub: string }).sub;\n }\n\n private async refresh(): Promise<void> {\n try {\n const tokens = await rawRequest<TokenPair>(this.creds.apiUrl, \"/v1/auth/refresh\", {\n method: \"POST\",\n body: { refresh_token: this.creds.refreshToken },\n headers: { \"X-Tenant-ID\": this.creds.tenantId },\n });\n this.creds = {\n ...this.creds,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n accessExpiresAt: expiryFrom(tokens),\n };\n saveCredentials(this.creds);\n } catch (err) {\n if (err instanceof ApiError && err.status >= 400 && err.status < 500) {\n throw new CliError(\n \"SESSION_EXPIRED\",\n \"the CLI login has expired\",\n \"Ask the human to run `npx @userbox/cli login` again.\",\n );\n }\n throw err;\n }\n }\n\n async request<T>(path: string, opts: RequestOptions & { envId?: string } = {}): Promise<T> {\n if (Date.now() > this.creds.accessExpiresAt - 30_000) await this.refresh();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.creds.accessToken}`,\n \"X-Tenant-ID\": this.creds.tenantId,\n ...opts.headers,\n };\n if (opts.envId) headers[\"X-Environment-ID\"] = opts.envId;\n try {\n return await rawRequest<T>(this.creds.apiUrl, path, { ...opts, headers });\n } catch (err) {\n if (err instanceof ApiError && err.status === 401) {\n await this.refresh();\n headers.Authorization = `Bearer ${this.creds.accessToken}`;\n return rawRequest<T>(this.creds.apiUrl, path, { ...opts, headers });\n }\n throw err;\n }\n }\n}\n","import { parse } from \"yaml\";\n\nimport { CliError } from \"./output.js\";\n\n// userbox.yaml: permissions and roles as code.\n//\n// permissions: [projects.read, projects.create, projects.delete]\n// roles:\n// admin: [projects.*]\n// member: [projects.read, projects.create]\n//\n// A role entry ending in `.*` expands to every declared permission with that\n// prefix. `apply` makes each declared role hold exactly its listed\n// permissions; permissions and roles that exist only in the environment are\n// reported, never deleted.\n\nexport interface Manifest {\n permissions: string[];\n roles: Record<string, string[]>;\n}\n\n// Same rule as the API: 2 to 5 lowercase segments separated by dots.\nconst PERMISSION_KEY = /^[a-z][a-z0-9_-]*(\\.[a-z][a-z0-9_-]*){1,4}$/;\n\nexport function parseManifest(text: string): Manifest {\n let raw: unknown;\n try {\n raw = parse(text);\n } catch (err) {\n throw new CliError(\"MANIFEST_INVALID\", `userbox.yaml is not valid YAML: ${(err as Error).message}`);\n }\n const doc = (raw ?? {}) as { permissions?: unknown; roles?: unknown };\n const permissions = doc.permissions ?? [];\n if (!Array.isArray(permissions) || permissions.some((p) => typeof p !== \"string\")) {\n throw new CliError(\"MANIFEST_INVALID\", \"`permissions` must be a list of strings\");\n }\n for (const p of permissions as string[]) {\n if (!PERMISSION_KEY.test(p)) {\n throw new CliError(\n \"MANIFEST_INVALID\",\n `invalid permission key \"${p}\"`,\n \"Use 2 to 5 lowercase segments separated by dots, for example projects.delete.\",\n );\n }\n }\n const rolesRaw = doc.roles ?? {};\n if (typeof rolesRaw !== \"object\" || Array.isArray(rolesRaw)) {\n throw new CliError(\"MANIFEST_INVALID\", \"`roles` must map role names to lists of permissions\");\n }\n const declared = new Set(permissions as string[]);\n const roles: Record<string, string[]> = {};\n for (const [name, list] of Object.entries(rolesRaw as Record<string, unknown>)) {\n if (!Array.isArray(list) || list.some((p) => typeof p !== \"string\")) {\n throw new CliError(\"MANIFEST_INVALID\", `role \"${name}\" must be a list of permission keys`);\n }\n const expanded = new Set<string>();\n for (const entry of list as string[]) {\n if (entry.endsWith(\".*\")) {\n const prefix = entry.slice(0, -1);\n const matches = [...declared].filter((p) => p.startsWith(prefix));\n if (matches.length === 0) {\n throw new CliError(\"MANIFEST_INVALID\", `role \"${name}\": \"${entry}\" matches no declared permission`);\n }\n matches.forEach((m) => expanded.add(m));\n } else if (!declared.has(entry)) {\n throw new CliError(\n \"MANIFEST_INVALID\",\n `role \"${name}\" uses \"${entry}\", which is not under \\`permissions\\``,\n );\n } else {\n expanded.add(entry);\n }\n }\n roles[name] = [...expanded].sort();\n }\n return { permissions: [...declared].sort(), roles };\n}\n\nexport interface RemoteState {\n permissions: string[];\n /** Role name → permission keys it holds now. */\n roles: Record<string, string[]>;\n}\n\nexport interface Plan {\n createPermissions: string[];\n createRoles: string[];\n grant: { role: string; permission: string }[];\n revoke: { role: string; permission: string }[];\n /** Exist in the environment but not in the file. Reported only. */\n extraPermissions: string[];\n extraRoles: string[];\n}\n\nexport function planApply(manifest: Manifest, remote: RemoteState): Plan {\n const remotePerms = new Set(remote.permissions);\n const plan: Plan = {\n createPermissions: manifest.permissions.filter((p) => !remotePerms.has(p)),\n createRoles: Object.keys(manifest.roles).filter((r) => !(r in remote.roles)).sort(),\n grant: [],\n revoke: [],\n extraPermissions: remote.permissions.filter((p) => !manifest.permissions.includes(p)).sort(),\n extraRoles: Object.keys(remote.roles).filter((r) => !(r in manifest.roles)).sort(),\n };\n for (const [role, wanted] of Object.entries(manifest.roles)) {\n const have = new Set(remote.roles[role] ?? []);\n for (const p of wanted) if (!have.has(p)) plan.grant.push({ role, permission: p });\n for (const p of have) if (!wanted.includes(p)) plan.revoke.push({ role, permission: p });\n }\n return plan;\n}\n\nexport function planIsEmpty(plan: Plan): boolean {\n return (\n plan.createPermissions.length === 0 &&\n plan.createRoles.length === 0 &&\n plan.grant.length === 0 &&\n plan.revoke.length === 0\n );\n}\n\n/** True when the plan takes a permission away from an existing role. */\nexport function planRevokes(plan: Plan): boolean {\n return plan.revoke.length > 0;\n}\n","import { createInterface } from \"node:readline\";\nimport { Writable } from \"node:stream\";\n\nimport { CliError } from \"./output.js\";\n\n/**\n * Interactive input exists only for a human at a terminal. Commands that need\n * a secret (the password) refuse to run without a TTY, so a coding agent\n * cannot type one and never sees it.\n */\nexport function requireTty(command: string): void {\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n throw new CliError(\n \"NEEDS_HUMAN\",\n `\\`userbox ${command}\\` must be run by a human in an interactive terminal`,\n `Ask the human to run \\`npx @userbox/cli ${command}\\` in their own terminal, then continue.`,\n );\n }\n}\n\nexport async function ask(question: string, { secret = false, fallback = \"\" } = {}): Promise<string> {\n let muted = false;\n const output = new Writable({\n write(chunk, _enc, cb) {\n if (!muted) process.stdout.write(chunk);\n cb();\n },\n });\n const rl = createInterface({ input: process.stdin, output, terminal: true });\n const label = fallback ? `${question} (${fallback}): ` : `${question}: `;\n try {\n return await new Promise<string>((resolve) => {\n rl.question(label, (answer) => {\n if (secret) process.stdout.write(\"\\n\");\n resolve(answer.trim() || fallback);\n });\n muted = secret;\n });\n } finally {\n rl.close();\n }\n}\n\nexport async function confirm(question: string): Promise<boolean> {\n const answer = await ask(`${question} [y/N]`);\n return /^y(es)?$/i.test(answer);\n}\n","import type { Session } from \"./api.js\";\nimport { CliError } from \"./output.js\";\n\n// Thin typed calls to the management API, as a tenant admin.\n\nexport interface Environment {\n id: string;\n name: string;\n type: \"production\" | \"staging\" | \"development\";\n isDefault: boolean;\n}\n\nexport interface Workspace {\n id: string;\n name: string;\n slug: string | null;\n status: string;\n}\n\nexport interface Permission {\n id: string;\n key: string;\n type: \"internal\" | \"external\";\n}\n\nexport interface Role {\n id: string;\n name: string;\n}\n\nexport interface AuthSettings {\n allowed_redirect_domains: string[] | null;\n allow_self_signup: boolean;\n}\n\nexport async function defaultEnvironment(s: Session): Promise<Environment> {\n const { environments } = await s.request<{ environments: Environment[] }>(\"/v1/environments\");\n const env = environments.find((e) => e.isDefault);\n if (!env) throw new CliError(\"NO_DEFAULT_ENVIRONMENT\", \"the tenant has no default environment\");\n return env;\n}\n\nexport function getAuthSettings(s: Session, env: Environment): Promise<AuthSettings> {\n return s.request<AuthSettings>(`/v1/environments/${env.id}/auth-settings`);\n}\n\n/** Adds `host` (a bare hostname such as `localhost`) to the allowed redirect domains. */\nexport async function ensureRedirectDomain(s: Session, env: Environment, host: string): Promise<boolean> {\n const settings = await getAuthSettings(s, env);\n const domains = settings.allowed_redirect_domains ?? [];\n if (domains.includes(host)) return false;\n await s.request(`/v1/environments/${env.id}/auth-settings`, {\n method: \"PATCH\",\n body: { allowed_redirect_domains: [...domains, host] },\n });\n return true;\n}\n\nexport async function createApiKey(\n s: Session,\n env: Environment,\n name: string,\n scopes: string[],\n): Promise<string> {\n const res = await s.request<{ key: string }>(\"/v1/api-keys\", {\n method: \"POST\",\n envId: env.id,\n body: { name, scopes },\n });\n return res.key;\n}\n\nexport async function listWorkspaces(s: Session, env: Environment): Promise<Workspace[]> {\n const res = await s.request<{ items: Workspace[] }>(\"/v1/workspaces?status=active&per_page=100\", {\n envId: env.id,\n });\n return res.items;\n}\n\nexport function createWorkspace(s: Session, env: Environment, name: string, slug: string): Promise<Workspace> {\n return s.request<Workspace>(\"/v1/workspaces\", { method: \"POST\", envId: env.id, body: { name, slug } });\n}\n\nexport async function listExternalPermissions(s: Session, env: Environment): Promise<Permission[]> {\n const all: Permission[] = [];\n let cursor: string | null = null;\n do {\n const q: string = cursor ? `&cursor=${encodeURIComponent(cursor)}` : \"\";\n const page: { items: Permission[]; next_cursor: string | null } = await s.request(\n `/v1/permissions?type=external&limit=200${q}`,\n { envId: env.id },\n );\n all.push(...page.items);\n cursor = page.next_cursor;\n } while (cursor);\n return all;\n}\n\nexport function createPermission(s: Session, env: Environment, key: string): Promise<Permission> {\n return s.request<Permission>(\"/v1/permissions\", { method: \"POST\", envId: env.id, body: { key, name: key } });\n}\n\nexport async function listRoles(s: Session, env: Environment, workspaceId: string): Promise<Role[]> {\n const res = await s.request<{ items: Role[] }>(`/v1/workspaces/${workspaceId}/roles?limit=200`, {\n envId: env.id,\n });\n return res.items;\n}\n\nexport function createRole(s: Session, env: Environment, workspaceId: string, name: string): Promise<Role> {\n return s.request<Role>(`/v1/workspaces/${workspaceId}/roles`, { method: \"POST\", envId: env.id, body: { name } });\n}\n\nexport interface RolePermission {\n permission_id: string;\n key: string;\n type: \"internal\" | \"external\";\n}\n\nexport async function listRolePermissions(\n s: Session,\n env: Environment,\n workspaceId: string,\n roleId: string,\n): Promise<RolePermission[]> {\n const res = await s.request<{ items: RolePermission[] }>(\n `/v1/workspaces/${workspaceId}/roles/${roleId}/permissions?limit=200`,\n { envId: env.id },\n );\n return res.items;\n}\n\nexport interface Membership {\n id: string;\n user_id: string | null;\n workspace_id: string;\n email: string | null;\n status: string;\n}\n\nexport async function listMemberships(s: Session, workspaceId: string): Promise<Membership[]> {\n const res = await s.request<{ items: Membership[] }>(\n `/v1/memberships?workspace_id=${workspaceId}&per_page=100`,\n );\n return res.items;\n}\n\nexport function inviteMember(s: Session, workspaceId: string, email: string): Promise<Membership> {\n return s.request<Membership>(\"/v1/memberships\", { method: \"POST\", body: { email, workspace_id: workspaceId } });\n}\n\nexport function assignRole(s: Session, membershipId: string, roleId: string): Promise<unknown> {\n return s.request(`/v1/memberships/${membershipId}/roles`, { method: \"POST\", body: { role_id: roleId } });\n}\nexport function grantRolePermission(\n s: Session,\n env: Environment,\n workspaceId: string,\n roleId: string,\n permissionId: string,\n): Promise<unknown> {\n return s.request(`/v1/workspaces/${workspaceId}/roles/${roleId}/permissions`, {\n method: \"POST\",\n envId: env.id,\n body: { permission_id: permissionId, effect: \"allow\" },\n });\n}\n\nexport function revokeRolePermission(\n s: Session,\n env: Environment,\n workspaceId: string,\n roleId: string,\n permissionId: string,\n): Promise<unknown> {\n return s.request(`/v1/workspaces/${workspaceId}/roles/${roleId}/permissions/${permissionId}`, {\n method: \"DELETE\",\n envId: env.id,\n });\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { Session } from \"../api.js\";\nimport type { Flags } from \"../bin.js\";\nimport type { Output } from \"../output.js\";\nimport {\n assignRole,\n createWorkspace,\n defaultEnvironment,\n inviteMember,\n listMemberships,\n listRoles,\n listWorkspaces,\n} from \"../userbox.js\";\nimport { confirmRevokes, executeSync, loadManifest, syncManifest } from \"./apply.js\";\n\nconst SLUG = \"dev\";\n\n// A dev workspace where the logged-in admin holds the `admin` role, plus\n// optional invited members. The API has no \"create user with password\"\n// route, so other users join by invitation email.\n\nexport async function devSeed(flags: Flags, out: Output): Promise<void> {\n const session = Session.load();\n const env = await defaultEnvironment(session);\n // A new tenant's default environment has type \"production\"; on the alpha\n // platform that is still test data, so warn instead of refusing.\n if (env.type === \"production\") {\n out.warn(`The default environment \"${env.name}\" is a production environment; the dev workspace is created there.`);\n }\n\n const workspaces = await listWorkspaces(session, env);\n let ws = workspaces.find((w) => w.slug === SLUG);\n if (ws) out.step(`Workspace ${SLUG} exists`);\n else {\n ws = await createWorkspace(session, env, \"Dev workspace\", SLUG);\n out.step(`Created workspace ${SLUG}`);\n }\n\n if (existsSync(join(process.cwd(), \"userbox.yaml\"))) {\n const sync = await syncManifest(session, env, loadManifest(process.cwd()), [ws]);\n if (await confirmRevokes(sync, flags, \"dev seed\")) {\n await executeSync(session, env, sync);\n out.step(\"Synced userbox.yaml roles into the workspace\");\n } else {\n out.warn(\"Skipped the userbox.yaml sync: nothing was removed from the roles.\");\n }\n } else {\n out.warn(\"No userbox.yaml: the workspace has no roles yet. Add one and run `userbox apply`.\");\n }\n\n const roles = new Map((await listRoles(session, env, ws.id)).map((r) => [r.name, r.id]));\n const memberships = await listMemberships(session, ws.id);\n const mine = memberships.find((m) => m.user_id === session.userId);\n let adminAssigned = false;\n const adminRole = roles.get(\"admin\");\n if (mine && adminRole) {\n await assignRole(session, mine.id, adminRole);\n adminAssigned = true;\n out.step(`You (${session.email}) have the admin role in ${SLUG}`);\n } else if (!adminRole) {\n out.warn(\"No `admin` role in userbox.yaml, so your membership has no role.\");\n } else {\n out.warn(\"You have no membership in the dev workspace; it was created by another user.\");\n }\n\n const invited: { email: string; role: string | null }[] = [];\n for (const spec of flags.invite) {\n const [email, roleName = \"member\"] = spec.split(\":\");\n if (!email) continue;\n const already = memberships.find((m) => m.email?.toLowerCase() === email.toLowerCase());\n const membership = already ?? (await inviteMember(session, ws.id, email));\n const roleId = roles.get(roleName);\n if (roleId) await assignRole(session, membership.id, roleId);\n else out.warn(`No role \"${roleName}\" for ${email}; invited without a role.`);\n invited.push({ email, role: roleId ? roleName : null });\n out.step(already ? `${email} is already a member` : `Invited ${email} as ${roleName}`);\n }\n\n out.done(\n { workspace: { id: ws.id, slug: SLUG }, environment: { id: env.id, name: env.name }, adminAssigned, invited },\n [\n `Dev workspace: ${ws.id}`,\n \"Sign in to your app with your Userbox login; you are admin in this workspace.\",\n ...(flags.invite.length === 0\n ? [\"Add a second user with `userbox dev seed --invite someone@example.com:member` (they get an email).\"]\n : []),\n ],\n );\n}\n","import { randomUUID } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { ApiError, Session, rawRequest } from \"../api.js\";\nimport type { Flags } from \"../bin.js\";\nimport { loadCredentials } from \"../credentials.js\";\nimport { ENV_KEYS, parseEnv } from \"../envfile.js\";\nimport { CliError, type Output } from \"../output.js\";\nimport { detectNextProject } from \"../project.js\";\nimport { defaultEnvironment, getAuthSettings } from \"../userbox.js\";\n\ntype Status = \"pass\" | \"warn\" | \"fail\" | \"skip\";\n\ninterface Check {\n name: string;\n status: Status;\n message: string;\n hint?: string;\n}\n\nconst MIN = { \"@userbox/react\": 4, \"@userbox/node\": 1 } as const;\n\nfunction alphaNumber(version: string): number | null {\n const m = /^1\\.0\\.0-alpha\\.(\\d+)$/.exec(version);\n if (m) return Number(m[1]);\n return /^\\d+\\.\\d+\\.\\d+$/.test(version) && !version.startsWith(\"0.\") ? Number.POSITIVE_INFINITY : null;\n}\n\nexport async function doctor(flags: Flags, out: Output): Promise<number> {\n const root = process.cwd();\n const checks: Check[] = [];\n const add = (c: Check) => {\n checks.push(c);\n const mark = { pass: \"✓\", warn: \"!\", fail: \"✗\", skip: \"-\" }[c.status];\n out.step(`${mark} ${c.name}: ${c.message}${c.hint && c.status !== \"pass\" ? `\\n → ${c.hint}` : \"\"}`);\n };\n\n // Project and files\n let appDir = \"app\";\n try {\n const project = detectNextProject(root);\n appDir = project.appDir;\n add({ name: \"project\", status: \"pass\", message: `Next.js ${project.nextVersion ?? \"\"} (${appDir})`.trim() });\n } catch (err) {\n add({ name: \"project\", status: \"fail\", message: (err as Error).message, hint: (err as CliError).hint });\n }\n\n for (const [name, min] of Object.entries(MIN)) {\n const pkgPath = join(root, \"node_modules\", name, \"package.json\");\n if (!existsSync(pkgPath)) {\n add({ name, status: \"fail\", message: \"not installed\", hint: \"Run `userbox init` or install @userbox/react@alpha @userbox/node@alpha.\" });\n continue;\n }\n const version = (JSON.parse(readFileSync(pkgPath, \"utf8\")) as { version: string }).version;\n const n = alphaNumber(version);\n add(\n n !== null && n >= min\n ? { name, status: \"pass\", message: version }\n : { name, status: \"fail\", message: `${version} is too old`, hint: `Install ${name}@alpha (1.0.0-alpha.${min} or newer).` },\n );\n }\n\n const files = [\n `${appDir}/api/auth/refresh/route.ts`,\n `${appDir}/auth/callback/page.tsx`,\n ];\n for (const f of files) {\n const found = [f, f.replace(/\\.tsx?$/, \".js\"), f.replace(/\\.tsx$/, \".jsx\")].some((p) => existsSync(join(root, p)));\n add(found\n ? { name: f, status: \"pass\", message: \"present\" }\n : { name: f, status: \"fail\", message: \"missing\", hint: \"Run `userbox init` to write it.\" });\n }\n\n // Environment file\n const envPath = join(root, \".env.local\");\n const env = existsSync(envPath) ? parseEnv(readFileSync(envPath, \"utf8\")) : new Map<string, string>();\n const missing = ENV_KEYS.filter((k) => !env.get(k));\n add(\n missing.length === 0\n ? { name: \".env.local\", status: \"pass\", message: \"all five variables set\" }\n : { name: \".env.local\", status: \"fail\", message: `missing ${missing.join(\", \")}`, hint: \"Run `userbox init` (it never overwrites existing values without --force).\" },\n );\n const leaked = [...env.entries()].filter(\n ([k, v]) => k.startsWith(\"NEXT_PUBLIC_\") && (/KEY|SECRET|TOKEN|PASSWORD/.test(k) || /^ub_(live|prod|stg|dev)_/.test(v)),\n );\n add(\n leaked.length === 0\n ? { name: \"secrets\", status: \"pass\", message: \"no secret in a NEXT_PUBLIC_ variable\" }\n : { name: \"secrets\", status: \"fail\", message: `${leaked.map(([k]) => k).join(\", \")} is exposed to the browser`, hint: \"Rename it without NEXT_PUBLIC_ and rotate the key in the console.\" },\n );\n const gitignore = existsSync(join(root, \".gitignore\")) ? readFileSync(join(root, \".gitignore\"), \"utf8\") : \"\";\n add(\n /^(\\.env\\*|\\.env\\.local|\\.env\\*\\.local|\\*\\.local)\\s*$/m.test(gitignore)\n ? { name: \".gitignore\", status: \"pass\", message: \".env.local is ignored\" }\n : { name: \".gitignore\", status: \"fail\", message: \".env.local is not ignored\", hint: \"Add `.env.local` to .gitignore before committing.\" },\n );\n\n // Live checks against the API\n const apiUrl = env.get(\"NEXT_PUBLIC_USERBOX_API_URL\");\n const apiKey = env.get(\"USERBOX_API_KEY\");\n const tenantId = env.get(\"USERBOX_TENANT_ID\");\n if (apiUrl) {\n try {\n const jwks = await rawRequest<{ keys: unknown[] }>(apiUrl, \"/.well-known/jwks.json\");\n add({ name: \"api\", status: jwks.keys.length > 0 ? \"pass\" : \"fail\", message: `${apiUrl} reachable, JWKS has ${jwks.keys.length} key(s)` });\n } catch (err) {\n add({ name: \"api\", status: \"fail\", message: (err as Error).message, hint: \"Check NEXT_PUBLIC_USERBOX_API_URL.\" });\n }\n }\n if (apiUrl && apiKey) {\n const auth = { Authorization: `Bearer ${apiKey}` };\n try {\n const t = await rawRequest<{ id: string; slug: string }>(apiUrl, \"/v1/tenants/me\", { headers: auth });\n const slugOk = t.slug === env.get(\"NEXT_PUBLIC_USERBOX_TENANT_SLUG\");\n add(\n t.id === tenantId && slugOk\n ? { name: \"api key\", status: \"pass\", message: `valid for tenant ${t.slug}` }\n : { name: \"api key\", status: \"fail\", message: `the key belongs to tenant ${t.slug} (${t.id}), not the one in .env.local`, hint: \"Fix USERBOX_TENANT_ID / NEXT_PUBLIC_USERBOX_TENANT_SLUG, or run `userbox init --force`.\" },\n );\n } catch (err) {\n add({ name: \"api key\", status: \"fail\", message: (err as Error).message, hint: \"The key is revoked or wrong: run `userbox init --force` for a new one.\" });\n }\n try {\n await rawRequest(apiUrl, \"/v1/access/check\", {\n method: \"POST\",\n headers: auth,\n body: { user_id: randomUUID(), workspace_id: randomUUID(), permission_key: \"doctor.probe\" },\n });\n add({ name: \"scope access:check\", status: \"pass\", message: \"granted\" });\n } catch (err) {\n const scope = err instanceof ApiError && err.code === \"INSUFFICIENT_SCOPE\";\n add(scope\n ? { name: \"scope access:check\", status: \"fail\", message: \"missing: can() always returns false\", hint: \"Run `userbox init --force` for a key with the right scopes.\" }\n : { name: \"scope access:check\", status: \"pass\", message: \"granted\" });\n }\n }\n\n // Checks that need the CLI login\n const creds = loadCredentials();\n if (!creds) {\n add({ name: \"redirect domain\", status: \"skip\", message: \"not logged in\", hint: \"Run `userbox login` to also check the tenant settings.\" });\n } else {\n try {\n const session = Session.load();\n if (tenantId && session.tenantId !== tenantId) {\n add({ name: \"login\", status: \"warn\", message: `logged in to ${session.tenantSlug}, but .env.local points at another tenant` });\n } else {\n const envr = await defaultEnvironment(session);\n const settings = await getAuthSettings(session, envr);\n const host = new URL(env.get(\"APP_ORIGIN\") ?? \"http://localhost:3000\").hostname;\n const domains = settings.allowed_redirect_domains ?? [];\n add(domains.includes(host)\n ? { name: \"redirect domain\", status: \"pass\", message: `${host} allowed` }\n : { name: \"redirect domain\", status: \"fail\", message: `${host} is not an allowed redirect domain`, hint: \"Run `userbox init`, or add it in Console → Auth settings.\" });\n add(settings.allow_self_signup\n ? { name: \"sign-up\", status: \"pass\", message: \"self-service sign-up is on\" }\n : { name: \"sign-up\", status: \"warn\", message: \"sign-up is invitation-only\", hint: \"Fine for dev. Turn on self-service sign-up in the console if new users should register.\" });\n }\n } catch (err) {\n add({ name: \"login\", status: \"warn\", message: (err as Error).message, hint: (err as CliError).hint });\n }\n }\n\n const failed = checks.filter((c) => c.status === \"fail\").length;\n out.done({ healthy: failed === 0, checks }, [\n failed === 0 ? \"\\nAll checks passed.\" : `\\n${failed} check(s) failed.`,\n ]);\n return failed === 0 ? 0 : 1;\n}\n","// Minimal .env reader/merger. It never rewrites or reorders existing lines:\n// values already present win unless the caller forces a key, and new keys are\n// appended in a block at the end.\n\nexport const ENV_KEYS = [\n \"NEXT_PUBLIC_USERBOX_TENANT_SLUG\",\n \"NEXT_PUBLIC_USERBOX_API_URL\",\n \"USERBOX_TENANT_ID\",\n \"USERBOX_API_KEY\",\n \"APP_ORIGIN\",\n] as const;\n\nexport type EnvKey = (typeof ENV_KEYS)[number];\n\nconst LINE = /^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*)$/;\n\nexport function parseEnv(text: string): Map<string, string> {\n const out = new Map<string, string>();\n for (const raw of text.split(/\\r?\\n/)) {\n const m = LINE.exec(raw);\n if (!m) continue;\n let value = (m[2] ?? \"\").trim();\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1);\n }\n out.set(m[1]!, value);\n }\n return out;\n}\n\nexport interface MergeResult {\n text: string;\n added: string[];\n replaced: string[];\n kept: string[];\n}\n\n/**\n * Merge `values` into an existing .env text. A key that already has a\n * non-empty value is kept, unless it is listed in `force`; then its line is\n * replaced in place. Missing or empty keys are written.\n */\nexport function mergeEnv(\n existing: string,\n values: Record<string, string>,\n force: ReadonlySet<string> = new Set(),\n): MergeResult {\n const current = parseEnv(existing);\n const added: string[] = [];\n const replaced: string[] = [];\n const kept: string[] = [];\n const lines = existing === \"\" ? [] : existing.replace(/\\n$/, \"\").split(/\\r?\\n/);\n const toAppend: string[] = [];\n\n for (const [key, value] of Object.entries(values)) {\n const had = current.get(key);\n if (had && !force.has(key)) {\n kept.push(key);\n continue;\n }\n const idx = lines.findIndex((l) => LINE.exec(l)?.[1] === key);\n if (idx >= 0) {\n lines[idx] = `${key}=${value}`;\n (had ? replaced : added).push(key);\n } else {\n toAppend.push(`${key}=${value}`);\n added.push(key);\n }\n }\n\n if (toAppend.length > 0) {\n if (lines.length > 0 && lines[lines.length - 1] !== \"\") lines.push(\"\");\n lines.push(\"# Userbox (written by `userbox init`)\", ...toAppend);\n }\n return { text: lines.length ? `${lines.join(\"\\n\")}\\n` : \"\", added, replaced, kept };\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { CliError } from \"./output.js\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nexport interface NextProject {\n root: string;\n /** \"app\" or \"src/app\". */\n appDir: string;\n packageManager: PackageManager;\n nextVersion: string | null;\n typescript: boolean;\n}\n\nexport function detectPackageManager(root: string): PackageManager {\n if (existsSync(join(root, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(root, \"yarn.lock\"))) return \"yarn\";\n if (existsSync(join(root, \"bun.lockb\")) || existsSync(join(root, \"bun.lock\"))) return \"bun\";\n return \"npm\";\n}\n\nexport function detectNextProject(root: string): NextProject {\n const pkgPath = join(root, \"package.json\");\n if (!existsSync(pkgPath)) {\n throw new CliError(\"NOT_A_PROJECT\", `no package.json in ${root}`, \"Run this in the root of your Next.js app.\");\n }\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (!deps.next) {\n throw new CliError(\n \"UNSUPPORTED_FRAMEWORK\",\n \"this is not a Next.js app (no `next` dependency)\",\n \"`userbox init` supports Next.js App Router only for now; see https://docs.test.userbox.app/llms.txt for other stacks.\",\n );\n }\n const appDir = existsSync(join(root, \"src\", \"app\")) ? \"src/app\" : existsSync(join(root, \"app\")) ? \"app\" : null;\n if (!appDir) {\n throw new CliError(\n \"UNSUPPORTED_FRAMEWORK\",\n \"no `app/` or `src/app/` directory: only the App Router is supported\",\n );\n }\n return {\n root,\n appDir,\n packageManager: detectPackageManager(root),\n nextVersion: deps.next ?? null,\n typescript: existsSync(join(root, \"tsconfig.json\")),\n };\n}\n\nexport function installCommand(pm: PackageManager, packages: string[]): [string, string[]] {\n switch (pm) {\n case \"pnpm\":\n return [\"pnpm\", [\"add\", ...packages]];\n case \"yarn\":\n return [\"yarn\", [\"add\", ...packages]];\n case \"bun\":\n return [\"bun\", [\"add\", ...packages]];\n default:\n return [\"npm\", [\"install\", ...packages]];\n }\n}\n","import { spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { Session } from \"../api.js\";\nimport type { Flags } from \"../bin.js\";\nimport { ENV_KEYS, mergeEnv, parseEnv } from \"../envfile.js\";\nimport { CliError, type Output } from \"../output.js\";\nimport { detectNextProject, installCommand } from \"../project.js\";\nimport { scaffold } from \"../scaffold.js\";\nimport { createApiKey, defaultEnvironment, ensureRedirectDomain } from \"../userbox.js\";\n\nexport const KEY_SCOPES = [\"access:check\", \"memberships:read\", \"workspaces:read\"];\nconst SDKS = [\"@userbox/react@alpha\", \"@userbox/node@alpha\"];\n\nfunction ensureGitignored(root: string): boolean {\n const path = join(root, \".gitignore\");\n const text = existsSync(path) ? readFileSync(path, \"utf8\") : \"\";\n const covered = text\n .split(/\\r?\\n/)\n .map((l) => l.trim())\n .some((l) => [\".env*\", \".env.local\", \".env*.local\", \"*.local\"].includes(l));\n if (covered) return false;\n writeFileSync(path, `${text}${text && !text.endsWith(\"\\n\") ? \"\\n\" : \"\"}.env.local\\n`);\n return true;\n}\n\nexport async function init(flags: Flags, out: Output): Promise<void> {\n const root = process.cwd();\n const project = detectNextProject(root);\n const session = Session.load();\n const origin = flags.origin ?? \"http://localhost:3000\";\n let host: string;\n try {\n host = new URL(origin).hostname;\n } catch {\n throw new CliError(\"INVALID_ORIGIN\", `--origin must be a URL such as http://localhost:3000, got \"${origin}\"`);\n }\n\n const env = await defaultEnvironment(session);\n out.step(`Tenant ${session.tenantSlug}, environment ${env.name} (${env.type})`);\n if (env.type === \"production\") out.warn(\"The default environment is production; the key created here is a production key.\");\n\n if (await ensureRedirectDomain(session, env, host)) out.step(`Added ${host} to the allowed redirect domains`);\n else out.step(`${host} is already an allowed redirect domain`);\n\n // .env.local: reuse an existing key unless --force; never print values.\n const envPath = join(root, \".env.local\");\n const existing = existsSync(envPath) ? readFileSync(envPath, \"utf8\") : \"\";\n const current = parseEnv(existing);\n const values: Record<string, string> = {\n NEXT_PUBLIC_USERBOX_TENANT_SLUG: session.tenantSlug,\n NEXT_PUBLIC_USERBOX_API_URL: session.apiUrl,\n USERBOX_TENANT_ID: session.tenantId,\n APP_ORIGIN: origin,\n };\n let keyCreated = false;\n if (flags.force || !current.get(\"USERBOX_API_KEY\")) {\n const date = new Date().toISOString().slice(0, 10);\n values.USERBOX_API_KEY = await createApiKey(session, env, `cli ${hostname()} ${date}`, KEY_SCOPES);\n keyCreated = true;\n out.step(`Created an API key with scopes ${KEY_SCOPES.join(\", \")}`);\n }\n const merged = mergeEnv(existing, values, flags.force ? new Set(ENV_KEYS) : new Set());\n writeFileSync(envPath, merged.text, { mode: 0o600 });\n out.step(`.env.local: ${merged.added.length} added, ${merged.replaced.length} replaced, ${merged.kept.length} kept`);\n const ignored = ensureGitignored(root);\n if (ignored) out.step(\"Added .env.local to .gitignore\");\n for (const key of merged.kept) {\n if (current.get(key) !== values[key] && key !== \"USERBOX_API_KEY\") {\n out.warn(`.env.local keeps a different ${key}; run with --force to replace it.`);\n }\n }\n\n let installed = false;\n if (!flags.skipInstall) {\n const [cmd, args] = installCommand(project.packageManager, SDKS);\n out.step(`Installing the SDKs: ${cmd} ${args.join(\" \")}`);\n const res = spawnSync(cmd, args, {\n cwd: root,\n stdio: flags.json ? [\"ignore\", \"ignore\", \"inherit\"] : \"inherit\",\n shell: process.platform === \"win32\",\n });\n if (res.status !== 0) {\n throw new CliError(\"INSTALL_FAILED\", `${cmd} exited with ${res.status ?? res.signal}`, `Run \\`${cmd} ${args.join(\" \")}\\` yourself.`);\n }\n installed = true;\n }\n\n const files = scaffold(project);\n for (const f of files.written) out.step(`Wrote ${f}`);\n for (const t of files.todo) out.warn(t);\n\n out.done(\n {\n tenant: { id: session.tenantId, slug: session.tenantSlug },\n environment: { id: env.id, name: env.name, type: env.type },\n redirectDomain: host,\n envFile: { path: \".env.local\", added: merged.added, replaced: merged.replaced, kept: merged.kept },\n apiKeyCreated: keyCreated,\n sdksInstalled: installed,\n files,\n next: [\"userbox apply\", \"userbox dev seed\", \"userbox doctor\"],\n },\n [\n \"\",\n \"Userbox is set up. Next:\",\n \" 1. Declare permissions and roles in userbox.yaml, then run `userbox apply`.\",\n \" 2. Run `userbox dev seed` for a workspace where you are admin.\",\n \" 3. Run `userbox doctor`, then start the app and sign in.\",\n \"Use verifyRequest() and can() from @userbox/node in your API routes (see https://docs.test.userbox.app/llms.txt).\",\n ],\n );\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nimport type { NextProject } from \"./project.js\";\n\n// The auth glue from llms.txt, written only where no file exists yet. Existing\n// files are never overwritten; the layout is edited only when the change is\n// unambiguous.\n\nexport interface ScaffoldResult {\n written: string[];\n skipped: string[];\n /** Things the CLI could not do safely; the human or agent does them. */\n todo: string[];\n}\n\nfunction providers(ts: boolean): string {\n const props = ts ? \"{ children }: { children: React.ReactNode }\" : \"{ children }\";\n const bang = ts ? \"!\" : \"\";\n return `\"use client\";\nimport { UserboxProvider } from \"@userbox/react\";\n\nexport function Providers(${props}) {\n return (\n <UserboxProvider\n tenantSlug={process.env.NEXT_PUBLIC_USERBOX_TENANT_SLUG${bang}}\n baseUrl={process.env.NEXT_PUBLIC_USERBOX_API_URL${bang}}\n tokenStorage=\"cookie\"\n refreshUrl=\"/api/auth/refresh\"\n >\n {children}\n </UserboxProvider>\n );\n}\n`;\n}\n\nconst CALLBACK = `\"use client\";\nimport { useRouter } from \"next/navigation\";\nimport { UserboxCallback } from \"@userbox/react\";\n\nexport default function Callback() {\n const router = useRouter();\n return <UserboxCallback onSuccess={() => router.replace(\"/\")} onError={(e) => console.error(e.message)} />;\n}\n`;\n\nconst REFRESH = `import { createRefreshHandler } from \"@userbox/react/nextjs\";\n\nexport const { POST } = createRefreshHandler();\n`;\n\nfunction writeIfMissing(root: string, rel: string, content: string, result: ScaffoldResult): void {\n const path = join(root, rel);\n if (existsSync(path)) {\n result.skipped.push(rel);\n return;\n }\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, content);\n result.written.push(rel);\n}\n\n/**\n * Wrap `{children}` in the root layout with `<Providers>`. Returns the new\n * text, or null when the layout already uses Providers or the edit is not\n * obvious (zero or several `{children}`).\n */\nexport function wrapLayout(text: string, importPath: string): string | null {\n if (/<Providers[\\s>]/.test(text)) return null;\n const occurrences = text.split(\"{children}\").length - 1;\n if (occurrences !== 1) return null;\n const wrapped = text.replace(\"{children}\", \"<Providers>{children}</Providers>\");\n const importLine = `import { Providers } from \"${importPath}\";\\n`;\n const lastImport = [...wrapped.matchAll(/^import .*$/gm)].pop();\n if (!lastImport || lastImport.index === undefined) return importLine + wrapped;\n const at = lastImport.index + lastImport[0].length;\n return `${wrapped.slice(0, at)}\\n${importLine.trimEnd()}${wrapped.slice(at)}`;\n}\n\nexport function scaffold(project: NextProject): ScaffoldResult {\n const result: ScaffoldResult = { written: [], skipped: [], todo: [] };\n const { root, appDir, typescript: ts } = project;\n const x = ts ? \"tsx\" : \"jsx\";\n const s = ts ? \"ts\" : \"js\";\n\n writeIfMissing(root, `${appDir}/providers.${x}`, providers(ts), result);\n writeIfMissing(root, `${appDir}/auth/callback/page.${x}`, CALLBACK, result);\n writeIfMissing(root, `${appDir}/api/auth/refresh/route.${s}`, REFRESH, result);\n\n const layoutRel = [`${appDir}/layout.tsx`, `${appDir}/layout.jsx`, `${appDir}/layout.js`].find((p) =>\n existsSync(join(root, p)),\n );\n if (!layoutRel) {\n result.todo.push(`Wrap {children} in your root layout with <Providers> from ./providers.`);\n return result;\n }\n const layoutPath = join(root, layoutRel);\n const layout = readFileSync(layoutPath, \"utf8\");\n if (/<Providers[\\s>]/.test(layout)) {\n result.skipped.push(layoutRel);\n return result;\n }\n const next = wrapLayout(layout, \"./providers\");\n if (next === null) {\n result.todo.push(`Wrap {children} in ${layoutRel} with <Providers> (import { Providers } from \"./providers\").`);\n } else {\n writeFileSync(layoutPath, next);\n result.written.push(`${layoutRel} (wrapped {children} with <Providers>)`);\n }\n return result;\n}\n","import { DEFAULT_API_URL, Session, expiryFrom, rawRequest, type TokenPair } from \"../api.js\";\nimport type { Flags } from \"../bin.js\";\nimport { clearCredentials, loadCredentials, saveCredentials } from \"../credentials.js\";\nimport type { Output } from \"../output.js\";\nimport { ask, requireTty } from \"../prompt.js\";\n\ninterface Tenant {\n id: string;\n slug: string;\n name: string;\n}\n\nexport async function login(flags: Flags, out: Output): Promise<void> {\n requireTty(\"login\");\n const previous = loadCredentials();\n const apiUrl = (flags.apiUrl ?? previous?.apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, \"\");\n const tenantSlug = await ask(\"Tenant slug\", { fallback: previous?.tenantSlug ?? \"\" });\n const email = await ask(\"Email\", { fallback: previous?.email ?? \"\" });\n const password = await ask(\"Password\", { secret: true });\n\n const tokens = await rawRequest<TokenPair>(apiUrl, \"/v1/auth/login\", {\n method: \"POST\",\n body: { email, password, tenant_slug: tenantSlug },\n });\n const tenant = await rawRequest<Tenant>(apiUrl, \"/v1/tenants/me\", {\n headers: { Authorization: `Bearer ${tokens.access_token}` },\n });\n saveCredentials({\n apiUrl,\n tenantSlug: tenant.slug,\n tenantId: tenant.id,\n email,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n accessExpiresAt: expiryFrom(tokens),\n });\n out.done({ tenant: { id: tenant.id, slug: tenant.slug }, email, apiUrl }, [\n `Logged in to ${tenant.name} (${tenant.slug}) as ${email}.`,\n \"The login is stored in ~/.config/userbox/credentials.json and lasts 7 days of inactivity.\",\n ]);\n}\n\nexport function logout(out: Output): void {\n clearCredentials();\n out.done({}, [\"Logged out.\"]);\n}\n\nexport async function whoami(out: Output): Promise<void> {\n const session = Session.load();\n const tenant = await session.request<Tenant>(\"/v1/tenants/me\");\n out.done(\n { tenant: { id: tenant.id, slug: tenant.slug }, email: session.email, apiUrl: session.apiUrl },\n [`${session.email} on ${tenant.name} (${tenant.slug}), ${session.apiUrl}`],\n );\n}\n"],"mappings":"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","names":["readFileSync","join","join","readFileSync","existsSync","join","existsSync","join","existsSync","readFileSync","join","existsSync","readFileSync","join","existsSync","join","readFileSync","join","existsSync","readFileSync","existsSync","readFileSync","writeFileSync","join","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","writeFileSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@userbox/cli",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.1",
4
4
  "description": "Userbox command-line tool: log in, set up a Next.js app, sync permissions and roles, seed dev users.",
5
5
  "type": "module",
6
6
  "bin": {