@abloatai/cli 0.41.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.cjs +998 -632
  2. package/package.json +3 -3
package/dist/cli.cjs CHANGED
@@ -3047,23 +3047,28 @@ function readProjectWriteDatabaseUrl(cwd = process.cwd()) {
3047
3047
  return readProjectEnvValue("ABLO_WRITE_DATABASE_URL", cwd);
3048
3048
  }
3049
3049
  function readProjectEnvValue(variable, cwd) {
3050
+ return readProjectEnvVariable(variable, cwd, false)?.value ?? null;
3051
+ }
3052
+ function readProjectEnvVariable(variable, cwd = process.cwd(), includeProcess = true) {
3053
+ if (includeProcess && process.env[variable]) {
3054
+ return { value: process.env[variable], source: "env" };
3055
+ }
3050
3056
  for (const filename of [".env.local", ".env"]) {
3051
3057
  const path = (0, import_path.resolve)(cwd, filename);
3052
3058
  if (!(0, import_fs2.existsSync)(path)) continue;
3053
3059
  const match = new RegExp(`^${variable}=(.+)$`, "m").exec((0, import_fs2.readFileSync)(path, "utf8"));
3054
- if (match?.[1]) return match[1].trim().replace(/^["']|["']$/g, "");
3060
+ if (match?.[1]) {
3061
+ return {
3062
+ value: match[1].trim().replace(/^["']|["']$/g, ""),
3063
+ source: filename
3064
+ };
3065
+ }
3055
3066
  }
3056
3067
  return null;
3057
3068
  }
3058
3069
  function readProjectApiKey(cwd = process.cwd()) {
3059
- if (process.env.ABLO_API_KEY) return { key: process.env.ABLO_API_KEY, source: "env" };
3060
- for (const name of [".env.local", ".env"]) {
3061
- const path = (0, import_path.resolve)(cwd, name);
3062
- if (!(0, import_fs2.existsSync)(path)) continue;
3063
- const match = /^ABLO_API_KEY=(.+)$/m.exec((0, import_fs2.readFileSync)(path, "utf8"));
3064
- if (match?.[1]) return { key: match[1].trim().replace(/^["']|["']$/g, ""), source: name };
3065
- }
3066
- return null;
3070
+ const found = readProjectEnvVariable("ABLO_API_KEY", cwd);
3071
+ return found ? { key: found.value, source: found.source } : null;
3067
3072
  }
3068
3073
  var import_crypto2, import_fs2, import_path, REPLICATION_URL_VARS, ADMIN_URL_VAR;
3069
3074
  var init_dbRole = __esm({
@@ -3308,43 +3313,16 @@ function getKeyEntry(mode) {
3308
3313
  if (!cfg) return void 0;
3309
3314
  return cfg.profiles[activeProfileName(cfg)]?.[mode];
3310
3315
  }
3316
+ function getManagementKeyEntry() {
3317
+ const cfg = readConfig();
3318
+ if (!cfg) return void 0;
3319
+ return cfg.profiles[activeProfileName(cfg)]?.management;
3320
+ }
3311
3321
  function modeFromKey(key) {
3312
3322
  if (/^(sk|rk)_test_/.test(key)) return "sandbox";
3313
3323
  if (/^(sk|rk)_live_/.test(key)) return "production";
3314
3324
  return void 0;
3315
3325
  }
3316
- function prefix(key) {
3317
- return key ? key.slice(0, 12) : null;
3318
- }
3319
- function describeEffectiveKey(activeMode, envKey, storedEntry) {
3320
- const effectiveKey = envKey ?? storedEntry?.apiKey;
3321
- const keySource = envKey ? "env" : storedEntry ? "stored" : null;
3322
- const keyMode = effectiveKey ? modeFromKey(effectiveKey) ?? null : null;
3323
- const keyMatchesActiveMode = keyMode ? keyMode === activeMode : null;
3324
- const keyMatchesStoredActiveKey = envKey && storedEntry?.apiKey ? envKey === storedEntry.apiKey : null;
3325
- let keyMismatch = null;
3326
- if (keyMode && keyMode !== activeMode) {
3327
- const sourceLabel = envKey ? "ABLO_API_KEY" : "stored active key";
3328
- keyMismatch = {
3329
- code: "key_mode_mismatch",
3330
- message: `${sourceLabel} is a ${keyMode} key but the CLI mode is ${activeMode}. Requests use ${sourceLabel} (${prefix(effectiveKey)}...), not the active CLI mode.`
3331
- };
3332
- } else if (envKey && storedEntry?.apiKey && envKey !== storedEntry.apiKey) {
3333
- keyMismatch = {
3334
- code: "env_key_overrides_stored",
3335
- message: `ABLO_API_KEY (${prefix(envKey)}...) overrides the stored ${activeMode} key (${prefix(storedEntry.apiKey)}...).`
3336
- };
3337
- }
3338
- return {
3339
- keyPrefix: prefix(effectiveKey),
3340
- keySource,
3341
- keyMode,
3342
- storedKeyPrefix: prefix(storedEntry?.apiKey),
3343
- keyMatchesActiveMode,
3344
- keyMatchesStoredActiveKey,
3345
- keyMismatch
3346
- };
3347
- }
3348
3326
  function normalizeMode(value) {
3349
3327
  return normalizeStoredMode(value);
3350
3328
  }
@@ -3358,9 +3336,14 @@ function clearCredential() {
3358
3336
  }
3359
3337
  return removed;
3360
3338
  }
3361
- function resolveApiKey(modeOverride) {
3339
+ function resolveMutationApiKey(modeOverride) {
3362
3340
  return resolveKey({ purpose: "data", mode: modeOverride }).key;
3363
3341
  }
3342
+ function ambientEnvKeyNote(cwd) {
3343
+ const ambient = readProjectApiKey(cwd);
3344
+ if (!ambient || ambient.source === "env") return null;
3345
+ return `Note: ${ambient.source} in this directory holds an ABLO_API_KEY, which this command does not load \u2014 it reads only the process environment and your stored login, so a file cannot silently choose a branch. Export the variable, or pass \`--env-file ` + ambient.source + "` to a connect command, to choose it explicitly.";
3346
+ }
3364
3347
  function resolveManagementKey() {
3365
3348
  if (process.env.ABLO_MANAGEMENT_KEY) return process.env.ABLO_MANAGEMENT_KEY;
3366
3349
  const cfg = readConfig();
@@ -3388,7 +3371,7 @@ function resolveOrgManagementKey() {
3388
3371
  return void 0;
3389
3372
  }
3390
3373
  function guardActiveProjectKey() {
3391
- const { key, source } = resolveEffectiveApiKey();
3374
+ const { key, source } = resolveRuntimeApiKey();
3392
3375
  if (key != null && source != null && source !== "stored") {
3393
3376
  return { ok: true, activeProfile: DEFAULT_PROFILE, available: [] };
3394
3377
  }
@@ -3421,16 +3404,9 @@ function resolveKey(policy) {
3421
3404
  }
3422
3405
  return { key: void 0, source: null };
3423
3406
  }
3424
- function resolveEffectiveApiKey(modeOverride, cwd) {
3407
+ function resolveRuntimeApiKey(modeOverride, cwd) {
3425
3408
  return resolveKey({ purpose: "data", mode: modeOverride, scanEnvFiles: true, cwd });
3426
3409
  }
3427
- function resolvePushPlan() {
3428
- const { key, source } = resolveEffectiveApiKey();
3429
- if (key != null && source != null && source !== "stored") {
3430
- return { flow: modeFromKey(key) ?? getMode(), apiKey: key, source };
3431
- }
3432
- return { flow: getMode(), apiKey: key, source };
3433
- }
3434
3410
  var import_os2, import_path2, import_fs3, DEFAULT_PROFILE;
3435
3411
  var init_config = __esm({
3436
3412
  "src/config.ts"() {
@@ -3767,13 +3743,14 @@ async function confirmFromServer(opts) {
3767
3743
  const project = projectId ? await nameProject(projectId, identity.accountScope, opts) : null;
3768
3744
  return {
3769
3745
  organizationId: identity.accountScope,
3770
- environment: keyEnv,
3746
+ environment: identity.branchRoot === void 0 ? keyEnv : identity.branchRoot ? "production" : "sandbox",
3771
3747
  project,
3772
3748
  projectId,
3773
3749
  branchId: identity.branchId ?? null,
3774
3750
  branchRoot: identity.branchRoot
3775
3751
  };
3776
- } catch {
3752
+ } catch (error) {
3753
+ if (opts.strict) throw error;
3777
3754
  return null;
3778
3755
  }
3779
3756
  }
@@ -4017,6 +3994,7 @@ function parsePushArgs(argv) {
4017
3994
  let force = false;
4018
3995
  let yes = false;
4019
3996
  let dryRun = false;
3997
+ let envFile;
4020
3998
  const renames = [];
4021
3999
  const backfills = [];
4022
4000
  for (let i = 0; i < argv.length; i++) {
@@ -4031,6 +4009,9 @@ function parsePushArgs(argv) {
4031
4009
  case "--url":
4032
4010
  url = argv[++i] ?? url;
4033
4011
  break;
4012
+ case "--env-file":
4013
+ envFile = argv[++i] ?? envFile;
4014
+ break;
4034
4015
  case "--force":
4035
4016
  force = true;
4036
4017
  break;
@@ -4072,7 +4053,18 @@ function parsePushArgs(argv) {
4072
4053
  }
4073
4054
  }
4074
4055
  url = url.replace(/\/+$/, "");
4075
- return { schemaPath, exportName, url, apiKey: process.env.ABLO_API_KEY, force, renames, backfills, yes, dryRun };
4056
+ return {
4057
+ schemaPath,
4058
+ exportName,
4059
+ url,
4060
+ apiKey: process.env.ABLO_API_KEY,
4061
+ ...envFile ? { envFile } : {},
4062
+ force,
4063
+ renames,
4064
+ backfills,
4065
+ yes,
4066
+ dryRun
4067
+ };
4076
4068
  }
4077
4069
  function publicationGap(value) {
4078
4070
  if (typeof value !== "object" || value === null) return null;
@@ -4173,8 +4165,21 @@ function printPlan(local, remote) {
4173
4165
  console.log("");
4174
4166
  }
4175
4167
  async function confirmPush(args, target) {
4176
- const env = target.confirmed?.environment ?? target.keyEnv;
4177
- const isProd = env === "production";
4168
+ const confirmedRoot = target.confirmed?.branchRoot;
4169
+ const legacyEnv = target.keyEnv;
4170
+ if (confirmedRoot === void 0 && legacyEnv === null) {
4171
+ console.error(
4172
+ ` ${import_picocolors5.default.red("\u2717")} Refusing to deploy because the server did not confirm which branch this key targets.`
4173
+ );
4174
+ console.error(
4175
+ import_picocolors5.default.dim(
4176
+ ` Current ${import_picocolors5.default.bold("sk_")} keys do not encode live/test state. Check connectivity with ${import_picocolors5.default.bold("ablo whoami")} and retry; the CLI will not guess for a write.`
4177
+ )
4178
+ );
4179
+ process.exit(1);
4180
+ return;
4181
+ }
4182
+ const isProd = confirmedRoot ?? legacyEnv === "production";
4178
4183
  const tty = Boolean(process.stdout.isTTY && process.stdin.isTTY);
4179
4184
  if (isProd && !args.yes) {
4180
4185
  if (!tty) {
@@ -4197,7 +4202,10 @@ async function confirmPush(args, target) {
4197
4202
  return;
4198
4203
  }
4199
4204
  if (!isProd && !args.yes && tty) {
4200
- const ok = await ye({ message: `Apply to ${import_picocolors5.default.bold("sandbox")}?` });
4205
+ const branch = target.confirmed?.branchId;
4206
+ const ok = await ye({
4207
+ message: `Apply to development branch${branch ? ` ${import_picocolors5.default.bold(branch)}` : ""}?`
4208
+ });
4201
4209
  if (pD(ok) || !ok) {
4202
4210
  xe("Aborted.");
4203
4211
  process.exit(1);
@@ -4206,8 +4214,7 @@ async function confirmPush(args, target) {
4206
4214
  }
4207
4215
  function printPushTarget(target, schema) {
4208
4216
  const confirmed = target.confirmed;
4209
- const env = confirmed?.environment ?? target.keyEnv;
4210
- const envLabel = env === "production" ? import_picocolors5.default.bold("production") : env === "sandbox" ? import_picocolors5.default.bold("sandbox") : import_picocolors5.default.yellow("unknown env");
4217
+ const branchLabel = confirmed?.branchRoot === true ? import_picocolors5.default.bold("production root") : confirmed?.branchId ? `${import_picocolors5.default.bold("branch")} ${import_picocolors5.default.bold(confirmed.branchId)}` : target.keyEnv === "production" ? `${import_picocolors5.default.bold("production root")} ${import_picocolors5.default.yellow("(legacy key; unconfirmed)")}` : target.keyEnv === "sandbox" ? `${import_picocolors5.default.bold("development branch")} ${import_picocolors5.default.yellow("(legacy key; unconfirmed)")}` : import_picocolors5.default.red("unknown branch");
4211
4218
  let projectLabel;
4212
4219
  if (confirmed?.project) {
4213
4220
  const p2 = confirmed.project;
@@ -4220,7 +4227,7 @@ function printPushTarget(target, schema) {
4220
4227
  projectLabel = `${shown} ${import_picocolors5.default.yellow("(unconfirmed \u2014 server did not answer)")}`;
4221
4228
  }
4222
4229
  console.log(`
4223
- ${brand("ablo")} ${import_picocolors5.default.dim("push")} ${import_picocolors5.default.dim("\u2192")} ${envLabel}`);
4230
+ ${brand("ablo")} ${import_picocolors5.default.dim("push")} ${import_picocolors5.default.dim("\u2192")} ${branchLabel}`);
4224
4231
  if (confirmed?.organizationId) console.log(` ${import_picocolors5.default.dim("org")} ${import_picocolors5.default.dim(confirmed.organizationId)}`);
4225
4232
  console.log(` ${import_picocolors5.default.dim("project")} ${projectLabel}`);
4226
4233
  console.log(` ${import_picocolors5.default.dim("target")} ${import_picocolors5.default.dim(target.url)}`);
@@ -4249,6 +4256,8 @@ function describeKeySource(source) {
4249
4256
  return ".env";
4250
4257
  case "stored":
4251
4258
  return "ablo login";
4259
+ case "explicit-file":
4260
+ return "--env-file";
4252
4261
  }
4253
4262
  }
4254
4263
  async function push(argv) {
@@ -4259,16 +4268,29 @@ async function push(argv) {
4259
4268
  console.error(import_picocolors5.default.red(` ${err instanceof Error ? err.message : String(err)}`));
4260
4269
  process.exit(1);
4261
4270
  }
4271
+ if (args.envFile) {
4272
+ try {
4273
+ process.loadEnvFile(args.envFile);
4274
+ } catch (error) {
4275
+ throw new import_errors6.AbloValidationError(
4276
+ `could not load --env-file ${args.envFile}: ${error instanceof Error ? error.message : String(error)}`,
4277
+ { code: "cli_invalid_arguments" }
4278
+ );
4279
+ }
4280
+ args.apiKey = process.env.ABLO_API_KEY;
4281
+ }
4262
4282
  let keySource = "env";
4263
4283
  if (!args.apiKey) {
4264
- const resolved = resolveEffectiveApiKey();
4265
- args.apiKey = resolved.key;
4266
- keySource = resolved.source ?? "stored";
4284
+ args.apiKey = resolveMutationApiKey();
4285
+ keySource = args.apiKey ? "stored" : "env";
4286
+ } else if (args.envFile) {
4287
+ keySource = "explicit-file";
4267
4288
  }
4268
4289
  if (!args.apiKey) {
4290
+ const ambient = ambientEnvKeyNote();
4269
4291
  console.error(
4270
4292
  import_picocolors5.default.red(` No API key.`) + import_picocolors5.default.dim(
4271
- ` Run ${import_picocolors5.default.bold("npx ablo login")} \u2014 or set ${import_picocolors5.default.bold("ABLO_API_KEY")} (${import_picocolors5.default.bold("sk_test_")} = sandbox; ${import_picocolors5.default.bold("sk_live_")} = production).`
4293
+ ` Run ${import_picocolors5.default.bold("ablo login")}, or set ${import_picocolors5.default.bold("ABLO_API_KEY")} to a branch-bound ${import_picocolors5.default.bold("sk_")} credential.` + (ambient ? ` A project env file contains one; select it explicitly with ${import_picocolors5.default.bold("npx ablo push --env-file .env.local")}.` : "")
4272
4294
  )
4273
4295
  );
4274
4296
  process.exit(1);
@@ -4405,19 +4427,19 @@ async function push(argv) {
4405
4427
  } else if (code === "schema_provisioning_forbidden") {
4406
4428
  console.error(
4407
4429
  import_picocolors5.default.dim(
4408
- ` This is not a key problem \u2014 the push was authorized, but the target database refused to let the engine create tables (Postgres 42501). On the replication read path Ablo never runs DDL: register your database as a data source with ${import_picocolors5.default.bold("npx ablo connect register")}, and pushes to that environment record the schema as metadata only \u2014 no tables are created anywhere.`
4430
+ ` This is not a key problem \u2014 the push was authorized, but the target database refused to let the engine create tables (Postgres 42501). On the replication read path Ablo never runs DDL: register your database as a data source with ${import_picocolors5.default.bold("npx ablo connect register")}, and pushes to that branch record the schema as metadata only \u2014 no tables are created anywhere.`
4409
4431
  )
4410
4432
  );
4411
4433
  } else if (args.apiKey != null && (0, import_credentialPolicy.classifyCredentialKind)(args.apiKey) === "restricted") {
4412
4434
  console.error(
4413
4435
  import_picocolors5.default.dim(
4414
- ` Schema pushes need a SECRET key: ${import_picocolors5.default.bold("sk_test_")} (sandbox dev loop) or a dashboard ${import_picocolors5.default.bold("sk_live_")} (production deploy: ${import_picocolors5.default.bold("ABLO_API_KEY=sk_live_\u2026 npx ablo push")}).`
4436
+ ` Schema pushes need a branch-bound SECRET ${import_picocolors5.default.bold("sk_")} key. Use ${import_picocolors5.default.bold("ablo dev")} for a development child or ${import_picocolors5.default.bold("ablo push")} with a root-bound key for production.`
4415
4437
  )
4416
4438
  );
4417
4439
  } else {
4418
4440
  console.error(
4419
4441
  import_picocolors5.default.dim(
4420
- ` This key isn't authorized to push schema (needs ${import_picocolors5.default.bold("schema:push")}). ` + (keySource === "stored" ? `It's your stored ${import_picocolors5.default.bold("ablo login")} sandbox key \u2014 a key in ${import_picocolors5.default.bold(".env.local")} or ${import_picocolors5.default.bold("ABLO_API_KEY")} takes precedence, so put a schema:push key there (sandbox ${import_picocolors5.default.bold("sk_test_")} or production ${import_picocolors5.default.bold("sk_live_")}) and re-push. ` : `Use a schema:push key \u2014 a sandbox ${import_picocolors5.default.bold("sk_test_")} or production ${import_picocolors5.default.bold("sk_live_")}. `) + `Manage keys at https://abloatai.com`
4442
+ ` This key isn't authorized to push schema (needs ${import_picocolors5.default.bold("schema:push")}). ` + (keySource === "stored" ? `The stored login is management-only. Put a branch-bound ${import_picocolors5.default.bold("sk_")} key with ${import_picocolors5.default.bold("schema:push")} in ${import_picocolors5.default.bold(".env.local")} or ${import_picocolors5.default.bold("ABLO_API_KEY")} and retry. ` : `Use a branch-bound ${import_picocolors5.default.bold("sk_")} key with ${import_picocolors5.default.bold("schema:push")}. `) + `Manage keys at https://abloatai.com`
4421
4443
  )
4422
4444
  );
4423
4445
  }
@@ -4426,7 +4448,7 @@ async function push(argv) {
4426
4448
  }
4427
4449
  process.exit(1);
4428
4450
  }
4429
- var import_picocolors5, import_errors6, import_credentialPolicy, import_fs4, import_path3, import_child_process, import_schema, import_schema2, DEFAULT_SCHEMA_PATH, DEFAULT_EXPORT;
4451
+ var import_picocolors5, import_errors6, import_credentialPolicy, import_fs4, import_path3, import_child_process, import_schema, import_schema2, DEFAULT_SCHEMA_PATH, DEFAULT_EXPORT, PUSH_USAGE;
4430
4452
  var init_push = __esm({
4431
4453
  "src/push.ts"() {
4432
4454
  "use strict";
@@ -4447,6 +4469,26 @@ var init_push = __esm({
4447
4469
  import_schema2 = require("@abloatai/transaction/coordination/schema");
4448
4470
  DEFAULT_SCHEMA_PATH = "ablo/schema.ts";
4449
4471
  DEFAULT_EXPORT = "schema";
4472
+ PUSH_USAGE = ` ablo push \u2014 upload a schema to one confirmed branch
4473
+
4474
+ Usage:
4475
+ npx ablo push
4476
+ npx ablo push --env-file .env.production --yes
4477
+ npx ablo push --dry-run
4478
+
4479
+ Credential:
4480
+ ABLO_API_KEY Branch-bound sk_ key from the process environment
4481
+ --env-file <path> Explicitly load ABLO_API_KEY from a dotenv file
4482
+
4483
+ Safety:
4484
+ --yes, -y Confirm non-interactively (required for the production root)
4485
+ --dry-run, --plan Show target and schema diff without applying
4486
+ --force Allow destructive schema changes
4487
+ --rename old:new Record a model rename
4488
+ --backfill m.f=value Seed existing rows for a new required field
4489
+
4490
+ The server confirms the key's project and branch. Current keys do not encode
4491
+ live/test state, and push never guesses a branch from their spelling.`;
4450
4492
  }
4451
4493
  });
4452
4494
 
@@ -4971,8 +5013,8 @@ __export(disconnect_exports, {
4971
5013
  function planeLabel(target) {
4972
5014
  const confirmed = target.confirmed;
4973
5015
  const project = confirmed?.project?.name ?? confirmed?.project?.slug ?? confirmed?.projectId ?? "the default project";
4974
- const env = confirmed?.environment ?? target.keyEnv ?? "unknown environment";
4975
- return { project, env };
5016
+ const branch = confirmed?.branchRoot === true ? "production root" : confirmed?.branchId ? `branch ${confirmed.branchId}` : target.keyEnv === "production" ? "production root (legacy key; unconfirmed)" : target.keyEnv === "sandbox" ? "development branch (legacy key; unconfirmed)" : "unknown branch";
5017
+ return { project, branch };
4976
5018
  }
4977
5019
  async function deregisterDataSource(opts) {
4978
5020
  try {
@@ -4989,7 +5031,7 @@ async function deregisterDataSource(opts) {
4989
5031
  if (err instanceof import_errors8.AbloError && err.code === "entity_not_found") return { removed: false };
4990
5032
  if (err instanceof import_errors8.AbloError && err.code === "forbidden") {
4991
5033
  throw new import_errors8.AbloPermissionError(
4992
- `${err.message}. Disconnecting needs a secret key (sk_\u2026) \u2014 run \`ablo login\` to store one.`,
5034
+ `${err.message}. Disconnecting needs a branch-bound secret key (sk_\u2026).`,
4993
5035
  {
4994
5036
  code: "forbidden",
4995
5037
  ...err.requestId !== void 0 ? { requestId: err.requestId } : {}
@@ -4999,7 +5041,7 @@ async function deregisterDataSource(opts) {
4999
5041
  throw err;
5000
5042
  }
5001
5043
  }
5002
- function renderDisconnected(response, project, envLabel) {
5044
+ function renderDisconnected(response, project, branchLabel) {
5003
5045
  const parts = [];
5004
5046
  if (response.cleared.direct) parts.push("the direct database registration");
5005
5047
  if (response.cleared.endpoints > 0) {
@@ -5010,7 +5052,7 @@ function renderDisconnected(response, project, envLabel) {
5010
5052
  const what = parts.length > 0 ? parts.join(" and ") : "the data source";
5011
5053
  console.log(
5012
5054
  `
5013
- ${import_picocolors8.default.green("\u2713")} Disconnected ${what} for ${import_picocolors8.default.bold(project)} in ${envLabel}. Reconnect with ${import_picocolors8.default.bold("ablo connect")}.
5055
+ ${import_picocolors8.default.green("\u2713")} Disconnected ${what} for ${import_picocolors8.default.bold(project)} on ${branchLabel}. Reconnect with ${import_picocolors8.default.bold("ablo connect")}.
5014
5056
  `
5015
5057
  );
5016
5058
  const slot = response.replication_slot;
@@ -5019,14 +5061,27 @@ function renderDisconnected(response, project, envLabel) {
5019
5061
  ` ${import_picocolors8.default.yellow("!")} ${slot.warning ?? `The replication slot ${import_picocolors8.default.bold(slot.slot)} is still on your database and still holding your write-ahead log. Remove it yourself \u2014 nothing will release it now.`}`
5020
5062
  );
5021
5063
  if (slot.detail) console.log(import_picocolors8.default.dim(` ${slot.detail}`));
5022
- if (slot.remove_with) console.log(` ${import_picocolors8.default.bold(slot.remove_with)}`);
5064
+ console.log(
5065
+ import_picocolors8.default.dim(
5066
+ ` Moving this database to another branch? Keep the roles and slot; run ${import_picocolors8.default.bold("ablo connect rotate --env-file .env.local --yes")} to re-key and reuse them.`
5067
+ )
5068
+ );
5069
+ if (slot.remove_with) {
5070
+ console.log(import_picocolors8.default.dim(" Not reconnecting this database? Remove the unused slot in your SQL editor:"));
5071
+ console.log(` ${import_picocolors8.default.bold(slot.remove_with)}`);
5072
+ }
5023
5073
  console.log();
5024
5074
  }
5025
5075
  }
5026
5076
  async function disconnect(argv) {
5027
5077
  let skipConfirm = false;
5028
- for (const arg of argv) {
5078
+ let envFile;
5079
+ let keyEnv;
5080
+ for (let i = 0; i < argv.length; i++) {
5081
+ const arg = argv[i];
5029
5082
  if (arg === "--yes" || arg === "-y") skipConfirm = true;
5083
+ else if (arg === "--env-file") envFile = argv[++i];
5084
+ else if (arg === "--key-env") keyEnv = argv[++i];
5030
5085
  else if (arg === "--help" || arg === "-h") {
5031
5086
  console.log(DISCONNECT_USAGE);
5032
5087
  return;
@@ -5042,19 +5097,39 @@ async function disconnect(argv) {
5042
5097
  ${brand("ablo")} ${import_picocolors8.default.dim("connect deregister")} ${import_picocolors8.default.dim("remove this project's data source")}
5043
5098
  `
5044
5099
  );
5045
- const resolved = resolveEffectiveApiKey();
5100
+ if (envFile) {
5101
+ try {
5102
+ process.loadEnvFile(envFile);
5103
+ } catch (error) {
5104
+ throw new import_errors8.AbloValidationError(
5105
+ `could not load --env-file ${envFile}: ${error instanceof Error ? error.message : String(error)}`,
5106
+ { code: "cli_invalid_arguments" }
5107
+ );
5108
+ }
5109
+ }
5110
+ const selected = keyEnv ? readProjectEnvVariable(keyEnv) : null;
5111
+ if (keyEnv && !selected) {
5112
+ throw new import_errors8.AbloAuthenticationError(
5113
+ `No value named ${keyEnv} was found in the process environment, .env.local, or .env.`,
5114
+ { code: "cli_api_key_missing" }
5115
+ );
5116
+ }
5117
+ const resolved = selected ? { key: selected.value, source: selected.source } : {
5118
+ key: resolveMutationApiKey(),
5119
+ source: envFile ? "explicit-file" : process.env.ABLO_API_KEY ? "env" : "stored"
5120
+ };
5046
5121
  const apiKey = resolved.key;
5047
5122
  const keySource = resolved.source ?? "stored";
5048
5123
  if (!apiKey) {
5049
5124
  throw new import_errors8.AbloAuthenticationError(
5050
- "Disconnecting needs an API key, and none was found. Run `ablo login` (or set ABLO_API_KEY), then re-run `ablo connect deregister`.",
5125
+ "Disconnecting needs a branch-bound sk_ key. Set ABLO_API_KEY, pass `--env-file .env.local`, or select a named recovery key with `--key-env <NAME>`.",
5051
5126
  { code: "cli_api_key_missing" }
5052
5127
  );
5053
5128
  }
5054
5129
  const apiUrl3 = apiBaseUrl();
5055
5130
  const target = await resolveTarget({ url: apiUrl3, apiKey, keySource });
5056
- const { project, env } = planeLabel(target);
5057
- const envLabel = env === "production" ? import_picocolors8.default.yellow(env) : import_picocolors8.default.dim(env);
5131
+ const { project, branch } = planeLabel(target);
5132
+ const branchLabel = target.confirmed?.branchRoot ? import_picocolors8.default.yellow(branch) : import_picocolors8.default.dim(branch);
5058
5133
  const divergence = describeMismatches(target.mismatches);
5059
5134
  if (divergence) console.log(` ${import_picocolors8.default.yellow("\u26A0")} ${divergence}
5060
5135
  `);
@@ -5066,7 +5141,7 @@ async function disconnect(argv) {
5066
5141
  );
5067
5142
  }
5068
5143
  const proceed = await ye({
5069
- message: `Disconnect the data source for ${import_picocolors8.default.bold(project)} in ${envLabel}?`,
5144
+ message: `Disconnect the data source for ${import_picocolors8.default.bold(project)} on ${branchLabel}?`,
5070
5145
  initialValue: true
5071
5146
  });
5072
5147
  if (pD(proceed) || !proceed) {
@@ -5077,11 +5152,11 @@ async function disconnect(argv) {
5077
5152
  }
5078
5153
  const outcome = await deregisterDataSource({ apiKey });
5079
5154
  if (!outcome.removed) {
5080
- console.log(import_picocolors8.default.dim(` No data source registered for ${project} in ${env}.
5155
+ console.log(import_picocolors8.default.dim(` No data source registered for ${project} on ${branch}.
5081
5156
  `));
5082
5157
  return;
5083
5158
  }
5084
- renderDisconnected(outcome.response, project, envLabel);
5159
+ renderDisconnected(outcome.response, project, branchLabel);
5085
5160
  }
5086
5161
  var import_picocolors8, import_errors8, import_wire4, DISCONNECT_USAGE;
5087
5162
  var init_disconnect = __esm({
@@ -5093,6 +5168,7 @@ var init_disconnect = __esm({
5093
5168
  import_errors8 = require("@abloatai/transaction/errors");
5094
5169
  import_wire4 = require("@abloatai/transaction/wire");
5095
5170
  init_config();
5171
+ init_dbRole();
5096
5172
  init_controlPlane();
5097
5173
  init_theme();
5098
5174
  init_target();
@@ -5101,9 +5177,10 @@ var init_disconnect = __esm({
5101
5177
  Usage
5102
5178
  npx ablo connect deregister Remove the active project's data source (confirms first)
5103
5179
  npx ablo connect deregister --yes Skip the confirmation
5180
+ npx ablo connect deregister --key-env OLD_KEY_NAME --yes
5104
5181
 
5105
- Acts on one plane \u2014 the active project (${import_picocolors8.default.bold("ablo projects use")}) in the key's
5106
- environment (its ${import_picocolors8.default.bold("sk_test_")}/${import_picocolors8.default.bold("sk_live_")} prefix), shown before it runs. Removes the registration and
5182
+ Acts on exactly the server-confirmed project and branch bound to the key, shown
5183
+ before it runs. Removes the registration and
5107
5184
  Ablo's replication state for that plane, so Ablo stops reading and writing the
5108
5185
  database. Reconnect with ${import_picocolors8.default.bold("ablo connect")}.`;
5109
5186
  }
@@ -5600,7 +5677,8 @@ function rotateWithoutConnection(input) {
5600
5677
  return "Ablo did not accept this API key, so it cannot be told about a new password. Rotate changes the password in your database first, so running it with a key Ablo refuses would leave the database on a password nobody holds.";
5601
5678
  }
5602
5679
  if (!input.known || input.planeHasConnection) return null;
5603
- return "This plane has no connected database, so there is no credential to re-key. Rotate changes the password in your database before Ablo can be told about it, so running it here would leave the database on a password Ablo never receives.";
5680
+ if (input.existingRoles.length > 0) return null;
5681
+ return "This plane has no connected database and Ablo's roles are not in this database, so there is no credential to re-key. Connecting for the first time is `ablo connect apply`, which creates the roles and registers them in one run.";
5604
5682
  }
5605
5683
  async function locateExistingConnection(input) {
5606
5684
  const result = await tryControlPlane({
@@ -5678,7 +5756,7 @@ async function executePlan(sql, steps, rebuildPlaintext) {
5678
5756
  async function runConnectApply(args) {
5679
5757
  const rotating = args.rotate;
5680
5758
  const verb = rotating ? "connect rotate" : "connect apply";
5681
- const adminUrl = args.url ?? readProjectAdminDatabaseUrl();
5759
+ let adminUrl = args.url ?? readProjectAdminDatabaseUrl();
5682
5760
  if (!adminUrl) {
5683
5761
  throw new import_errors9.AbloValidationError(
5684
5762
  "No admin connection string. Pass --url <admin-conn> (or set DATABASE_URL) and re-run.",
@@ -5692,15 +5770,22 @@ async function runConnectApply(args) {
5692
5770
  target = `${parsed.host}${parsed.pathname === "/" ? "" : parsed.pathname}`;
5693
5771
  } catch {
5694
5772
  }
5695
- const apiKey = resolveApiKey();
5773
+ const apiKey = resolveMutationApiKey();
5696
5774
  if (!apiKey) {
5697
5775
  const loggedIn = resolveManagementKey() !== void 0;
5776
+ const ambient = ambientEnvKeyNote();
5777
+ const retry = `npx ablo connect ${rotating ? "rotate" : "apply"} --env-file .env.local --yes`;
5698
5778
  throw new import_errors9.AbloAuthenticationError(
5699
- loggedIn ? `You are logged in, but this project has no ${getMode()} data key.
5779
+ loggedIn ? `You are logged in, but connect needs a branch-bound runtime key.
5780
+
5781
+ Logging in stores an mk_ management credential; it can manage branches but cannot read, write, or register a database. Set ABLO_API_KEY to the sk_ key for the exact branch this database should join. The server confirms whether that branch is the production root or a development child; the CLI does not infer it from the key spelling.${ambient ? `
5700
5782
 
5701
- The key is looked for in ABLO_API_KEY, then in the credential stored for the active project in the active mode (${getMode()}). Logging in stores a management credential, which administers the project but cannot register a database \u2014 and a key for another mode is never used implicitly, so registering against production takes an explicit production key.
5783
+ ${ambient}
5702
5784
 
5703
- Mint a sandbox key with \`npx ablo dev\`, or set ABLO_API_KEY to the key of the plane this database should join (its sk_live_ key for production).` : "Not logged in, and no ABLO_API_KEY is set. Run `ablo login` (or set ABLO_API_KEY) so Ablo knows which project to register this database for.",
5785
+ Use it explicitly with:
5786
+ ${retry}` : ""}` : `Not logged in, and no ABLO_API_KEY is set. Run \`ablo login\` (or set ABLO_API_KEY) so Ablo knows which project to register this database for.${ambient ? `
5787
+
5788
+ ${ambient}` : ""}`,
5704
5789
  { code: "cli_api_key_missing" }
5705
5790
  );
5706
5791
  }
@@ -5715,23 +5800,39 @@ Mint a sandbox key with \`npx ablo dev\`, or set ABLO_API_KEY to the key of the
5715
5800
  );
5716
5801
  const pooledAdmin = detectPooler(adminUrl);
5717
5802
  if (pooledAdmin?.confidence === "host") {
5718
- console.error(
5719
- ` ${import_picocolors11.default.yellow("!")} ${import_picocolors11.default.bold(target)} is a connection pooler, not the database.
5803
+ if (pooledAdmin.direct) {
5804
+ adminUrl = pooledAdmin.direct;
5805
+ const pooledLabel = target;
5806
+ try {
5807
+ const parsed = new URL(adminUrl);
5808
+ target = `${parsed.host}${parsed.pathname === "/" ? "" : parsed.pathname}`;
5809
+ } catch {
5810
+ }
5811
+ console.log(
5812
+ ` ${import_picocolors11.default.yellow("!")} ${import_picocolors11.default.bold(pooledLabel)} is a connection pooler, so this run uses the direct host:
5813
+ ${import_picocolors11.default.bold(target)}
5814
+ ` + import_picocolors11.default.dim(
5815
+ ` A pooler terminates the session, so replication cannot run over it. Your app
5816
+ keeps the pooled URL; the setup needs the database itself.
5720
5817
  `
5721
- );
5722
- console.error(
5723
- import_picocolors11.default.dim(
5724
- ` A pooler terminates the session, so replication cannot run over it. Setting up
5725
- through it creates roles that Ablo then cannot use to stream.
5818
+ )
5819
+ );
5820
+ } else {
5821
+ console.error(
5822
+ ` ${import_picocolors11.default.yellow("!")} ${import_picocolors11.default.bold(target)} is a connection pooler, not the database.
5726
5823
  `
5727
- )
5728
- );
5729
- console.error(
5730
- pooledAdmin.direct ? ` Re-run against the direct host: ${import_picocolors11.default.bold(pooledAdmin.direct)}
5731
- ` : ` Re-run against the direct database host, not the pooled one.
5824
+ );
5825
+ console.error(
5826
+ import_picocolors11.default.dim(
5827
+ ` A pooler terminates the session, so replication cannot run over it. Setting up
5828
+ through it creates roles that Ablo then cannot use to stream.
5732
5829
  `
5733
- );
5734
- process.exit(1);
5830
+ )
5831
+ );
5832
+ console.error(` Re-run against the direct database host, not the pooled one.
5833
+ `);
5834
+ process.exit(1);
5835
+ }
5735
5836
  }
5736
5837
  if (pooledAdmin?.confidence === "port") {
5737
5838
  console.log(
@@ -5771,31 +5872,31 @@ Mint a sandbox key with \`npx ablo dev\`, or set ABLO_API_KEY to the key of the
5771
5872
  `);
5772
5873
  console.error(
5773
5874
  import_picocolors11.default.dim(` Your database is untouched.
5774
- `) + ` To move it here, disconnect it there first with ${import_picocolors11.default.cyan("ablo connect deregister")}.
5775
- `
5875
+ `) + ` To move it here, disconnect it there first with ${import_picocolors11.default.cyan("ablo connect deregister")}
5876
+ ` + import_picocolors11.default.dim(` (run with a key for that plane). Match the project id to a name with `) + import_picocolors11.default.bold("ablo projects list --json") + import_picocolors11.default.dim(".") + "\n"
5776
5877
  );
5777
5878
  process.exit(1);
5778
5879
  }
5880
+ let rotatePlane = null;
5779
5881
  if (rotating) {
5780
5882
  const state = await fetchDataSourceState(apiBaseUrl(), apiKey).catch(
5781
5883
  () => ({ kind: "unknown", detail: "unreachable" })
5782
5884
  );
5783
- const refusal = rotateWithoutConnection({
5784
- rotating,
5885
+ rotatePlane = {
5785
5886
  planeHasConnection: state.kind === "connected",
5786
5887
  known: state.kind !== "unknown",
5787
5888
  // 401/403 is Ablo answering and declining the key, not a network failure.
5788
5889
  keyRejected: state.kind === "unknown" && /HTTP 40[13]/.test(state.detail)
5789
- });
5790
- if (refusal) {
5791
- console.error(` ${import_picocolors11.default.yellow("!")} ${refusal}
5890
+ };
5891
+ if (rotatePlane.keyRejected) {
5892
+ const refusal = rotateWithoutConnection({ rotating, ...rotatePlane, existingRoles: [] });
5893
+ if (refusal) {
5894
+ console.error(` ${import_picocolors11.default.yellow("!")} ${refusal}
5792
5895
  `);
5793
- console.error(
5794
- import_picocolors11.default.dim(` Your database is untouched.
5795
- `) + ` Connect it instead: ${import_picocolors11.default.cyan("npx ablo connect apply")}
5796
- `
5797
- );
5798
- process.exit(1);
5896
+ console.error(import_picocolors11.default.dim(` Your database is untouched.
5897
+ `));
5898
+ process.exit(1);
5899
+ }
5799
5900
  }
5800
5901
  }
5801
5902
  const admin = src_default(adminUrl, {
@@ -5855,9 +5956,21 @@ Mint a sandbox key with \`npx ablo dev\`, or set ABLO_API_KEY to the key of the
5855
5956
  const pubReconcile = reconcilePublicationPlan(existingPublication, tables);
5856
5957
  const role = args.role && args.role.length > 0 ? args.role : import_footprint.ABLO_REPLICATION_ROLE;
5857
5958
  const writeRole = args.writeRole && args.writeRole.length > 0 ? args.writeRole : import_footprint.ABLO_WRITE_ROLE;
5959
+ const existingRoles = await presentRoles(admin, [role, writeRole]).catch(() => []);
5960
+ if (rotatePlane) {
5961
+ const refusal = rotateWithoutConnection({ rotating, ...rotatePlane, existingRoles });
5962
+ if (refusal) {
5963
+ await admin.end({ timeout: 2 });
5964
+ console.error(` ${import_picocolors11.default.yellow("!")} ${refusal}
5965
+ `);
5966
+ console.error(import_picocolors11.default.dim(` Your database is untouched.
5967
+ `));
5968
+ process.exit(1);
5969
+ }
5970
+ }
5858
5971
  const blocker = reapplyBlocker({
5859
5972
  rotating,
5860
- existingRoles: await presentRoles(admin, [role, writeRole]).catch(() => [])
5973
+ existingRoles
5861
5974
  });
5862
5975
  if (blocker) {
5863
5976
  await admin.end({ timeout: 2 });
@@ -6077,9 +6190,11 @@ function parseConnectArgs(argv) {
6077
6190
  let apply = false;
6078
6191
  let rotate = false;
6079
6192
  let url;
6193
+ let envFile;
6080
6194
  let yes = false;
6081
6195
  let showSql = false;
6082
6196
  let scan = false;
6197
+ let locate = false;
6083
6198
  let manual = false;
6084
6199
  let tables = [];
6085
6200
  let role = import_footprint.ABLO_REPLICATION_ROLE;
@@ -6104,9 +6219,12 @@ function parseConnectArgs(argv) {
6104
6219
  case "scan":
6105
6220
  scan = true;
6106
6221
  break;
6222
+ case "locate":
6223
+ locate = true;
6224
+ break;
6107
6225
  default:
6108
6226
  throw new import_errors10.AbloValidationError(
6109
- `unknown connect subcommand: ${lead} (expected register, deregister, check, apply, rotate, scan)`,
6227
+ `unknown connect subcommand: ${lead} (expected register, deregister, check, apply, rotate, scan, locate)`,
6110
6228
  { code: "cli_invalid_arguments" }
6111
6229
  );
6112
6230
  }
@@ -6118,6 +6236,9 @@ function parseConnectArgs(argv) {
6118
6236
  case "--url":
6119
6237
  url = argv[++i] ?? url;
6120
6238
  break;
6239
+ case "--env-file":
6240
+ envFile = argv[++i] ?? envFile;
6241
+ break;
6121
6242
  case "--yes":
6122
6243
  case "-y":
6123
6244
  yes = true;
@@ -6165,9 +6286,11 @@ function parseConnectArgs(argv) {
6165
6286
  apply,
6166
6287
  rotate,
6167
6288
  url,
6289
+ envFile,
6168
6290
  yes,
6169
6291
  showSql,
6170
6292
  scan,
6293
+ locate,
6171
6294
  tables,
6172
6295
  role,
6173
6296
  writeRole,
@@ -6463,10 +6586,13 @@ async function runCheck() {
6463
6586
  ${brand("ablo")} ${import_picocolors12.default.dim("connect check")} ${import_picocolors12.default.dim("direct-write + WAL readiness")}
6464
6587
  `
6465
6588
  );
6466
- const apiKey = resolveApiKey();
6589
+ const apiKey = resolveRuntimeApiKey().key;
6467
6590
  if (!apiKey) {
6591
+ const ambient = ambientEnvKeyNote();
6468
6592
  throw new import_errors10.AbloAuthenticationError(
6469
- "No API key found. Run `ablo login` (or set ABLO_API_KEY), then re-run `ablo connect check`.",
6593
+ `No branch-bound runtime key found. Set ABLO_API_KEY to the sk_ key for the branch to check, or pass \`--env-file .env.local\` explicitly.${ambient ? `
6594
+
6595
+ ${ambient}` : ""}`,
6470
6596
  { code: "cli_api_key_missing" }
6471
6597
  );
6472
6598
  }
@@ -6529,10 +6655,13 @@ async function runCheck() {
6529
6655
  async function runRegister(args) {
6530
6656
  const dbUrl = requireScopedUrl("replication", "register");
6531
6657
  const writeDbUrl = requireScopedUrl("write", "register");
6532
- const apiKey = resolveApiKey();
6658
+ const apiKey = resolveMutationApiKey();
6533
6659
  if (!apiKey) {
6660
+ const ambient = ambientEnvKeyNote();
6534
6661
  throw new import_errors10.AbloAuthenticationError(
6535
- "Not logged in. Run `ablo login` (or set ABLO_API_KEY) so Ablo knows which project to register this database for.",
6662
+ `No branch-bound runtime key found. Set ABLO_API_KEY to the sk_ key for the branch to register, or pass \`--env-file .env.local\` explicitly.${ambient ? `
6663
+
6664
+ ${ambient}` : ""}`,
6536
6665
  { code: "cli_api_key_missing" }
6537
6666
  );
6538
6667
  }
@@ -6633,6 +6762,62 @@ async function runScan() {
6633
6762
  }
6634
6763
  process.exit(retired.length > 0 ? 1 : 0);
6635
6764
  }
6765
+ async function runLocate(args) {
6766
+ console.log(
6767
+ `
6768
+ ${brand("ablo")} ${import_picocolors12.default.dim("connect locate")} ${import_picocolors12.default.dim("which plane holds this database")}
6769
+ `
6770
+ );
6771
+ const url = args.url ?? readProjectAdminDatabaseUrl();
6772
+ if (!url) {
6773
+ throw new import_errors10.AbloValidationError(
6774
+ "Locating needs a connection string to identify the database. Pass --url <conn> (or set DATABASE_URL) and re-run.",
6775
+ { code: "cli_database_url_missing" }
6776
+ );
6777
+ }
6778
+ const apiKey = resolveRuntimeApiKey().key;
6779
+ if (!apiKey) {
6780
+ const ambient = ambientEnvKeyNote();
6781
+ throw new import_errors10.AbloAuthenticationError(
6782
+ `No branch-bound runtime key found. Set ABLO_API_KEY to a key that can inspect this project, or pass \`--env-file .env.local\` explicitly.${ambient ? `
6783
+
6784
+ ${ambient}` : ""}`,
6785
+ { code: "cli_api_key_missing" }
6786
+ );
6787
+ }
6788
+ let label = "this database";
6789
+ try {
6790
+ const parsed = new URL(url);
6791
+ label = `${parsed.host}${parsed.pathname === "/" ? "" : parsed.pathname}`;
6792
+ } catch {
6793
+ }
6794
+ const answer = await requestControlPlane({
6795
+ path: "/v1/datasources/locate",
6796
+ method: "POST",
6797
+ apiKey,
6798
+ body: { connectionString: url },
6799
+ responseSchema: import_wire7.datasourceLocationResponseSchema
6800
+ });
6801
+ if (!answer.held) {
6802
+ console.log(
6803
+ ` ${import_picocolors12.default.green("\u2713")} No plane holds ${import_picocolors12.default.bold(label)} \u2014 ${import_picocolors12.default.bold("ablo connect apply")} can register it here.
6804
+ `
6805
+ );
6806
+ return;
6807
+ }
6808
+ console.log(
6809
+ ` ${import_picocolors12.default.yellow("!")} ${import_picocolors12.default.bold(label)} is connected to ${answer.held.project ? `project ${import_picocolors12.default.bold(answer.held.project)}, ` : ""}branch ${import_picocolors12.default.bold(answer.held.branch)}.
6810
+ `
6811
+ );
6812
+ console.log(
6813
+ import_picocolors12.default.dim(
6814
+ ` Ablo streams a database from one plane at a time. To move it, disconnect it there
6815
+ first \u2014 run `
6816
+ ) + import_picocolors12.default.cyan("ablo connect deregister") + import_picocolors12.default.dim(` with a key for that plane, then connect here.
6817
+ `) + import_picocolors12.default.dim(` Confirm a candidate key first with `) + import_picocolors12.default.bold("ablo whoami --key-env <NAME>") + import_picocolors12.default.dim(`.
6818
+ `) + import_picocolors12.default.dim(` Match the project id to a name with `) + import_picocolors12.default.bold("ablo projects list --json") + import_picocolors12.default.dim(".") + "\n"
6819
+ );
6820
+ }
6636
6821
  async function connect(argv) {
6637
6822
  if (argv[0] === "deregister") {
6638
6823
  const { disconnect: disconnect2 } = await Promise.resolve().then(() => (init_disconnect(), disconnect_exports));
@@ -6640,6 +6825,16 @@ async function connect(argv) {
6640
6825
  return;
6641
6826
  }
6642
6827
  const args = parseConnectArgs(argv);
6828
+ if (args.envFile) {
6829
+ try {
6830
+ process.loadEnvFile(args.envFile);
6831
+ } catch (error) {
6832
+ throw new import_errors10.AbloValidationError(
6833
+ `could not load --env-file ${args.envFile}: ${error instanceof Error ? error.message : String(error)}`,
6834
+ { code: "cli_invalid_arguments" }
6835
+ );
6836
+ }
6837
+ }
6643
6838
  if (args.apply || args.rotate) {
6644
6839
  const { runConnectApply: runConnectApply2 } = await Promise.resolve().then(() => (init_connectApply(), connectApply_exports));
6645
6840
  await runConnectApply2(args);
@@ -6657,6 +6852,10 @@ async function connect(argv) {
6657
6852
  await runScan();
6658
6853
  return;
6659
6854
  }
6855
+ if (args.locate) {
6856
+ await runLocate(args);
6857
+ return;
6858
+ }
6660
6859
  const credentialReachable = (args.url ?? readProjectAdminDatabaseUrl()) != null;
6661
6860
  const canConfirm = process.stdout.isTTY || args.yes;
6662
6861
  if (!args.manual && credentialReachable && canConfirm) {
@@ -6666,7 +6865,7 @@ async function connect(argv) {
6666
6865
  }
6667
6866
  printConnectRecipe(args);
6668
6867
  }
6669
- var import_errors10, import_picocolors12, import_footprint2, FOOTPRINT_LOOKUP, CONNECT_USAGE;
6868
+ var import_errors10, import_picocolors12, import_footprint2, import_wire7, FOOTPRINT_LOOKUP, CONNECT_USAGE;
6670
6869
  var init_connect = __esm({
6671
6870
  "src/connect.ts"() {
6672
6871
  "use strict";
@@ -6678,6 +6877,7 @@ var init_connect = __esm({
6678
6877
  init_dbRole();
6679
6878
  init_config();
6680
6879
  init_controlPlane();
6880
+ import_wire7 = require("@abloatai/transaction/wire");
6681
6881
  init_theme();
6682
6882
  init_remoteValidation();
6683
6883
  init_connectSetup();
@@ -6701,6 +6901,7 @@ var init_connect = __esm({
6701
6901
  npx ablo connect check Confirm the connected database is ready, from Ablo's side (needs only ABLO_API_KEY)
6702
6902
  npx ablo connect rotate New passwords for both logins, then re-register
6703
6903
  npx ablo connect scan List anything Ablo ever set up in your database (read-only, never drops)
6904
+ npx ablo connect locate See which plane holds this database (read-only; nothing is changed)
6704
6905
 
6705
6906
  Running it: bare \`ablo connect\` sets everything up for you \u2014 creating the two
6706
6907
  scoped logins, sharing your tables, and registering \u2014 whenever it finds a
@@ -6711,6 +6912,7 @@ var init_connect = __esm({
6711
6912
 
6712
6913
  Modifiers:
6713
6914
  --url <admin-conn> Admin connection used once to set up (else DATABASE_URL); never stored
6915
+ --env-file <path> Explicitly load DATABASE_URL and ABLO_API_KEY from this file
6714
6916
  --tables a,b,c Publish only these tables (default: all tables)
6715
6917
  --role <name> Name the replication role (default: ablo_replicator)
6716
6918
  --write-role <name> Name the DML role (default: ablo_writer)
@@ -12601,8 +12803,8 @@ var require_typescript = __commonJS({
12601
12803
  function createGetCanonicalFileName(useCaseSensitiveFileNames2) {
12602
12804
  return useCaseSensitiveFileNames2 ? identity : toFileNameLowerCase;
12603
12805
  }
12604
- function patternText({ prefix: prefix2, suffix }) {
12605
- return `${prefix2}*${suffix}`;
12806
+ function patternText({ prefix, suffix }) {
12807
+ return `${prefix}*${suffix}`;
12606
12808
  }
12607
12809
  function matchedText(pattern, candidate) {
12608
12810
  Debug.assert(isPatternMatch(pattern, candidate));
@@ -12621,17 +12823,17 @@ var require_typescript = __commonJS({
12621
12823
  }
12622
12824
  return matchedValue;
12623
12825
  }
12624
- function startsWith(str, prefix2, ignoreCase) {
12625
- return ignoreCase ? equateStringsCaseInsensitive(str.slice(0, prefix2.length), prefix2) : str.lastIndexOf(prefix2, 0) === 0;
12826
+ function startsWith(str, prefix, ignoreCase) {
12827
+ return ignoreCase ? equateStringsCaseInsensitive(str.slice(0, prefix.length), prefix) : str.lastIndexOf(prefix, 0) === 0;
12626
12828
  }
12627
- function removePrefix(str, prefix2) {
12628
- return startsWith(str, prefix2) ? str.substr(prefix2.length) : str;
12829
+ function removePrefix(str, prefix) {
12830
+ return startsWith(str, prefix) ? str.substr(prefix.length) : str;
12629
12831
  }
12630
- function tryRemovePrefix(str, prefix2, getCanonicalFileName = identity) {
12631
- return startsWith(getCanonicalFileName(str), getCanonicalFileName(prefix2)) ? str.substring(prefix2.length) : void 0;
12832
+ function tryRemovePrefix(str, prefix, getCanonicalFileName = identity) {
12833
+ return startsWith(getCanonicalFileName(str), getCanonicalFileName(prefix)) ? str.substring(prefix.length) : void 0;
12632
12834
  }
12633
- function isPatternMatch({ prefix: prefix2, suffix }, candidate) {
12634
- return candidate.length >= prefix2.length + suffix.length && startsWith(candidate, prefix2) && endsWith(candidate, suffix);
12835
+ function isPatternMatch({ prefix, suffix }, candidate) {
12836
+ return candidate.length >= prefix.length + suffix.length && startsWith(candidate, prefix) && endsWith(candidate, suffix);
12635
12837
  }
12636
12838
  function and(f, g2) {
12637
12839
  return (arg) => f(arg) && g2(arg);
@@ -18405,8 +18607,8 @@ ${lanes.join("\n")}
18405
18607
  );
18406
18608
  const firstComponent = pathComponents2[0];
18407
18609
  if (isAbsolutePathAnUrl && isRootedDiskPath(firstComponent)) {
18408
- const prefix2 = firstComponent.charAt(0) === directorySeparator ? "file://" : "file:///";
18409
- pathComponents2[0] = prefix2 + firstComponent;
18610
+ const prefix = firstComponent.charAt(0) === directorySeparator ? "file://" : "file:///";
18611
+ pathComponents2[0] = prefix + firstComponent;
18410
18612
  }
18411
18613
  return getPathFromPathComponents(pathComponents2);
18412
18614
  }
@@ -35422,12 +35624,12 @@ ${lanes.join("\n")}
35422
35624
  node.symbol = void 0;
35423
35625
  return node;
35424
35626
  }
35425
- function createBaseGeneratedIdentifier(text, autoGenerateFlags, prefix2, suffix) {
35627
+ function createBaseGeneratedIdentifier(text, autoGenerateFlags, prefix, suffix) {
35426
35628
  const node = createBaseIdentifier(escapeLeadingUnderscores(text));
35427
35629
  setIdentifierAutoGenerate(node, {
35428
35630
  flags: autoGenerateFlags,
35429
35631
  id: nextAutoGenerateId,
35430
- prefix: prefix2,
35632
+ prefix,
35431
35633
  suffix
35432
35634
  });
35433
35635
  nextAutoGenerateId++;
@@ -35450,10 +35652,10 @@ ${lanes.join("\n")}
35450
35652
  }
35451
35653
  return node;
35452
35654
  }
35453
- function createTempVariable(recordTempVariable, reservedInNestedScopes, prefix2, suffix) {
35655
+ function createTempVariable(recordTempVariable, reservedInNestedScopes, prefix, suffix) {
35454
35656
  let flags2 = 1;
35455
35657
  if (reservedInNestedScopes) flags2 |= 8;
35456
- const name = createBaseGeneratedIdentifier("", flags2, prefix2, suffix);
35658
+ const name = createBaseGeneratedIdentifier("", flags2, prefix, suffix);
35457
35659
  if (recordTempVariable) {
35458
35660
  recordTempVariable(name);
35459
35661
  }
@@ -35471,23 +35673,23 @@ ${lanes.join("\n")}
35471
35673
  void 0
35472
35674
  );
35473
35675
  }
35474
- function createUniqueName(text, flags2 = 0, prefix2, suffix) {
35676
+ function createUniqueName(text, flags2 = 0, prefix, suffix) {
35475
35677
  Debug.assert(!(flags2 & 7), "Argument out of range: flags");
35476
35678
  Debug.assert((flags2 & (16 | 32)) !== 32, "GeneratedIdentifierFlags.FileLevel cannot be set without also setting GeneratedIdentifierFlags.Optimistic");
35477
- return createBaseGeneratedIdentifier(text, 3 | flags2, prefix2, suffix);
35679
+ return createBaseGeneratedIdentifier(text, 3 | flags2, prefix, suffix);
35478
35680
  }
35479
- function getGeneratedNameForNode(node, flags2 = 0, prefix2, suffix) {
35681
+ function getGeneratedNameForNode(node, flags2 = 0, prefix, suffix) {
35480
35682
  Debug.assert(!(flags2 & 7), "Argument out of range: flags");
35481
35683
  const text = !node ? "" : isMemberName(node) ? formatGeneratedName(
35482
35684
  /*privateName*/
35483
35685
  false,
35484
- prefix2,
35686
+ prefix,
35485
35687
  node,
35486
35688
  suffix,
35487
35689
  idText
35488
35690
  ) : `generated@${getNodeId(node)}`;
35489
- if (prefix2 || suffix) flags2 |= 16;
35490
- const name = createBaseGeneratedIdentifier(text, 4 | flags2, prefix2, suffix);
35691
+ if (prefix || suffix) flags2 |= 16;
35692
+ const name = createBaseGeneratedIdentifier(text, 4 | flags2, prefix, suffix);
35491
35693
  name.original = node;
35492
35694
  return name;
35493
35695
  }
@@ -35504,33 +35706,33 @@ ${lanes.join("\n")}
35504
35706
  if (!startsWith(text, "#")) Debug.fail("First character of private identifier must be #: " + text);
35505
35707
  return createBasePrivateIdentifier(escapeLeadingUnderscores(text));
35506
35708
  }
35507
- function createBaseGeneratedPrivateIdentifier(text, autoGenerateFlags, prefix2, suffix) {
35709
+ function createBaseGeneratedPrivateIdentifier(text, autoGenerateFlags, prefix, suffix) {
35508
35710
  const node = createBasePrivateIdentifier(escapeLeadingUnderscores(text));
35509
35711
  setIdentifierAutoGenerate(node, {
35510
35712
  flags: autoGenerateFlags,
35511
35713
  id: nextAutoGenerateId,
35512
- prefix: prefix2,
35714
+ prefix,
35513
35715
  suffix
35514
35716
  });
35515
35717
  nextAutoGenerateId++;
35516
35718
  return node;
35517
35719
  }
35518
- function createUniquePrivateName(text, prefix2, suffix) {
35720
+ function createUniquePrivateName(text, prefix, suffix) {
35519
35721
  if (text && !startsWith(text, "#")) Debug.fail("First character of private identifier must be #: " + text);
35520
35722
  const autoGenerateFlags = 8 | (text ? 3 : 1);
35521
- return createBaseGeneratedPrivateIdentifier(text ?? "", autoGenerateFlags, prefix2, suffix);
35723
+ return createBaseGeneratedPrivateIdentifier(text ?? "", autoGenerateFlags, prefix, suffix);
35522
35724
  }
35523
- function getGeneratedPrivateNameForNode(node, prefix2, suffix) {
35725
+ function getGeneratedPrivateNameForNode(node, prefix, suffix) {
35524
35726
  const text = isMemberName(node) ? formatGeneratedName(
35525
35727
  /*privateName*/
35526
35728
  true,
35527
- prefix2,
35729
+ prefix,
35528
35730
  node,
35529
35731
  suffix,
35530
35732
  idText
35531
35733
  ) : `#generated@${getNodeId(node)}`;
35532
- const flags2 = prefix2 || suffix ? 16 : 0;
35533
- const name = createBaseGeneratedPrivateIdentifier(text, 4 | flags2, prefix2, suffix);
35734
+ const flags2 = prefix || suffix ? 16 : 0;
35735
+ const name = createBaseGeneratedPrivateIdentifier(text, 4 | flags2, prefix, suffix);
35534
35736
  name.original = node;
35535
35737
  return name;
35536
35738
  }
@@ -40332,13 +40534,13 @@ ${lanes.join("\n")}
40332
40534
  [expr]
40333
40535
  );
40334
40536
  }
40335
- function createSetFunctionNameHelper(f, name, prefix2) {
40537
+ function createSetFunctionNameHelper(f, name, prefix) {
40336
40538
  context.requestEmitHelper(setFunctionNameHelper);
40337
40539
  return context.factory.createCallExpression(
40338
40540
  getUnscopedHelperName("__setFunctionName"),
40339
40541
  /*typeArguments*/
40340
40542
  void 0,
40341
- prefix2 ? [f, name, context.factory.createStringLiteral(prefix2)] : [f, name]
40543
+ prefix ? [f, name, context.factory.createStringLiteral(prefix)] : [f, name]
40342
40544
  );
40343
40545
  }
40344
40546
  function createValuesHelper(expression) {
@@ -42675,11 +42877,11 @@ ${lanes.join("\n")}
42675
42877
  function formatIdentifierWorker(node, generateName) {
42676
42878
  return isGeneratedPrivateIdentifier(node) ? generateName(node).slice(1) : isGeneratedIdentifier(node) ? generateName(node) : isPrivateIdentifier(node) ? node.escapedText.slice(1) : idText(node);
42677
42879
  }
42678
- function formatGeneratedName(privateName, prefix2, baseName, suffix, generateName) {
42679
- prefix2 = formatGeneratedNamePart(prefix2, generateName);
42880
+ function formatGeneratedName(privateName, prefix, baseName, suffix, generateName) {
42881
+ prefix = formatGeneratedNamePart(prefix, generateName);
42680
42882
  suffix = formatGeneratedNamePart(suffix, generateName);
42681
42883
  baseName = formatIdentifier(baseName, generateName);
42682
- return `${privateName ? "#" : ""}${prefix2}${baseName}${suffix}`;
42884
+ return `${privateName ? "#" : ""}${prefix}${baseName}${suffix}`;
42683
42885
  }
42684
42886
  function createAccessorPropertyBackingField(factory2, node, modifiers, initializer) {
42685
42887
  return factory2.updatePropertyDeclaration(
@@ -62754,11 +62956,11 @@ ${lanes.join("\n")}
62754
62956
  candidates.push({ ending: void 0, value: relativeToBaseUrl });
62755
62957
  }
62756
62958
  if (indexOfStar !== -1) {
62757
- const prefix2 = pattern.substring(0, indexOfStar);
62959
+ const prefix = pattern.substring(0, indexOfStar);
62758
62960
  const suffix = pattern.substring(indexOfStar + 1);
62759
62961
  for (const { ending, value } of candidates) {
62760
- if (value.length >= prefix2.length + suffix.length && startsWith(value, prefix2) && endsWith(value, suffix) && validateEnding({ ending, value })) {
62761
- const matchedStar = value.substring(prefix2.length, value.length - suffix.length);
62962
+ if (value.length >= prefix.length + suffix.length && startsWith(value, prefix) && endsWith(value, suffix) && validateEnding({ ending, value })) {
62963
+ const matchedStar = value.substring(prefix.length, value.length - suffix.length);
62762
62964
  if (!pathIsRelative(matchedStar)) {
62763
62965
  return replaceFirstStar(key, matchedStar);
62764
62966
  }
@@ -82457,9 +82659,9 @@ ${lanes.join("\n")}
82457
82659
  }
82458
82660
  secondaryRootErrors.unshift([mappedMsg, args[0], args[1]]);
82459
82661
  } else {
82460
- const prefix2 = msg.code === Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible.code || msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code ? "new " : "";
82662
+ const prefix = msg.code === Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible.code || msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code ? "new " : "";
82461
82663
  const params = msg.code === Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code || msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code ? "" : "...";
82462
- path = `${prefix2}${path}(${params})`;
82664
+ path = `${prefix}${path}(${params})`;
82463
82665
  }
82464
82666
  break;
82465
82667
  }
@@ -117301,13 +117503,13 @@ ${lanes.join("\n")}
117301
117503
  }
117302
117504
  function createHoistedVariableForClass(name, node, suffix) {
117303
117505
  const { className } = getPrivateIdentifierEnvironment().data;
117304
- const prefix2 = className ? { prefix: "_", node: className, suffix: "_" } : "_";
117305
- const identifier = typeof name === "object" ? factory2.getGeneratedNameForNode(name, 16 | 8, prefix2, suffix) : typeof name === "string" ? factory2.createUniqueName(name, 16, prefix2, suffix) : factory2.createTempVariable(
117506
+ const prefix = className ? { prefix: "_", node: className, suffix: "_" } : "_";
117507
+ const identifier = typeof name === "object" ? factory2.getGeneratedNameForNode(name, 16 | 8, prefix, suffix) : typeof name === "string" ? factory2.createUniqueName(name, 16, prefix, suffix) : factory2.createTempVariable(
117306
117508
  /*recordTempVariable*/
117307
117509
  void 0,
117308
117510
  /*reservedInNestedScopes*/
117309
117511
  true,
117310
- prefix2,
117512
+ prefix,
117311
117513
  suffix
117312
117514
  );
117313
117515
  if (resolver.hasNodeCheckFlag(
@@ -118380,7 +118582,7 @@ ${lanes.join("\n")}
118380
118582
  if (!decoratorExpressions) {
118381
118583
  return void 0;
118382
118584
  }
118383
- const prefix2 = getClassMemberPrefix(node, member);
118585
+ const prefix = getClassMemberPrefix(node, member);
118384
118586
  const memberName = getExpressionForPropertyName(
118385
118587
  member,
118386
118588
  /*generateNameForComputedPropertyName*/
@@ -118393,7 +118595,7 @@ ${lanes.join("\n")}
118393
118595
  const descriptor = isPropertyDeclaration(member) && !hasAccessorModifier(member) ? factory2.createVoidZero() : factory2.createNull();
118394
118596
  const helper = emitHelpers().createDecorateHelper(
118395
118597
  decoratorExpressions,
118396
- prefix2,
118598
+ prefix,
118397
118599
  memberName,
118398
118600
  descriptor
118399
118601
  );
@@ -120296,13 +120498,13 @@ ${lanes.join("\n")}
120296
120498
  3072
120297
120499
  /* NoComments */
120298
120500
  );
120299
- const prefix2 = kind === "get" || kind === "set" ? kind : void 0;
120501
+ const prefix = kind === "get" || kind === "set" ? kind : void 0;
120300
120502
  const functionName = factory2.createStringLiteralFromNode(
120301
120503
  name,
120302
120504
  /*isSingleQuote*/
120303
120505
  void 0
120304
120506
  );
120305
- const namedFunction = emitHelpers().createSetFunctionNameHelper(func, functionName, prefix2);
120507
+ const namedFunction = emitHelpers().createSetFunctionNameHelper(func, functionName, prefix);
120306
120508
  const method = factory2.createPropertyAssignment(factory2.createIdentifier(kind), namedFunction);
120307
120509
  setOriginalNode(method, original);
120308
120510
  setSourceMapRange(method, moveRangePastDecorators(original));
@@ -141082,9 +141284,9 @@ ${lanes.join("\n")}
141082
141284
  emitExpression(node, parenthesizerRule);
141083
141285
  }
141084
141286
  }
141085
- function emitNodeWithPrefix(prefix2, prefixWriter, node, emit2) {
141287
+ function emitNodeWithPrefix(prefix, prefixWriter, node, emit2) {
141086
141288
  if (node) {
141087
- prefixWriter(prefix2);
141289
+ prefixWriter(prefix);
141088
141290
  emit2(node);
141089
141291
  }
141090
141292
  }
@@ -141822,10 +142024,10 @@ ${lanes.join("\n")}
141822
142024
  return autoGeneratedIdToGeneratedName[autoGenerateId] || (autoGeneratedIdToGeneratedName[autoGenerateId] = makeName(name));
141823
142025
  }
141824
142026
  }
141825
- function generateNameCached(node, privateName, flags, prefix2, suffix) {
142027
+ function generateNameCached(node, privateName, flags, prefix, suffix) {
141826
142028
  const nodeId = getNodeId(node);
141827
142029
  const cache = privateName ? nodeIdToGeneratedPrivateName : nodeIdToGeneratedName;
141828
- return cache[nodeId] || (cache[nodeId] = generateNameForNode(node, privateName, flags ?? 0, formatGeneratedNamePart(prefix2, generateName), formatGeneratedNamePart(suffix)));
142030
+ return cache[nodeId] || (cache[nodeId] = generateNameForNode(node, privateName, flags ?? 0, formatGeneratedNamePart(prefix, generateName), formatGeneratedNamePart(suffix)));
141829
142031
  }
141830
142032
  function isUniqueName(name, privateName) {
141831
142033
  return isFileLevelUniqueNameInCurrentFile(name, privateName) && !isReservedName(name, privateName) && !generatedNames.has(name);
@@ -141892,15 +142094,15 @@ ${lanes.join("\n")}
141892
142094
  break;
141893
142095
  }
141894
142096
  }
141895
- function makeTempVariableName(flags, reservedInNestedScopes, privateName, prefix2, suffix) {
141896
- if (prefix2.length > 0 && prefix2.charCodeAt(0) === 35) {
141897
- prefix2 = prefix2.slice(1);
142097
+ function makeTempVariableName(flags, reservedInNestedScopes, privateName, prefix, suffix) {
142098
+ if (prefix.length > 0 && prefix.charCodeAt(0) === 35) {
142099
+ prefix = prefix.slice(1);
141898
142100
  }
141899
- const key = formatGeneratedName(privateName, prefix2, "", suffix);
142101
+ const key = formatGeneratedName(privateName, prefix, "", suffix);
141900
142102
  let tempFlags2 = getTempFlags(key);
141901
142103
  if (flags && !(tempFlags2 & flags)) {
141902
142104
  const name = flags === 268435456 ? "_i" : "_n";
141903
- const fullName = formatGeneratedName(privateName, prefix2, name, suffix);
142105
+ const fullName = formatGeneratedName(privateName, prefix, name, suffix);
141904
142106
  if (isUniqueName(fullName, privateName)) {
141905
142107
  tempFlags2 |= flags;
141906
142108
  if (privateName) {
@@ -141917,7 +142119,7 @@ ${lanes.join("\n")}
141917
142119
  tempFlags2++;
141918
142120
  if (count !== 8 && count !== 13) {
141919
142121
  const name = count < 26 ? "_" + String.fromCharCode(97 + count) : "_" + (count - 26);
141920
- const fullName = formatGeneratedName(privateName, prefix2, name, suffix);
142122
+ const fullName = formatGeneratedName(privateName, prefix, name, suffix);
141921
142123
  if (isUniqueName(fullName, privateName)) {
141922
142124
  if (privateName) {
141923
142125
  reservePrivateNameInNestedScopes(fullName);
@@ -141930,15 +142132,15 @@ ${lanes.join("\n")}
141930
142132
  }
141931
142133
  }
141932
142134
  }
141933
- function makeUniqueName2(baseName, checkFn = isUniqueName, optimistic, scoped, privateName, prefix2, suffix) {
142135
+ function makeUniqueName2(baseName, checkFn = isUniqueName, optimistic, scoped, privateName, prefix, suffix) {
141934
142136
  if (baseName.length > 0 && baseName.charCodeAt(0) === 35) {
141935
142137
  baseName = baseName.slice(1);
141936
142138
  }
141937
- if (prefix2.length > 0 && prefix2.charCodeAt(0) === 35) {
141938
- prefix2 = prefix2.slice(1);
142139
+ if (prefix.length > 0 && prefix.charCodeAt(0) === 35) {
142140
+ prefix = prefix.slice(1);
141939
142141
  }
141940
142142
  if (optimistic) {
141941
- const fullName = formatGeneratedName(privateName, prefix2, baseName, suffix);
142143
+ const fullName = formatGeneratedName(privateName, prefix, baseName, suffix);
141942
142144
  if (checkFn(fullName, privateName)) {
141943
142145
  if (privateName) {
141944
142146
  reservePrivateNameInNestedScopes(fullName);
@@ -141955,7 +142157,7 @@ ${lanes.join("\n")}
141955
142157
  }
141956
142158
  let i = 1;
141957
142159
  while (true) {
141958
- const fullName = formatGeneratedName(privateName, prefix2, baseName + i, suffix);
142160
+ const fullName = formatGeneratedName(privateName, prefix, baseName + i, suffix);
141959
142161
  if (checkFn(fullName, privateName)) {
141960
142162
  if (privateName) {
141961
142163
  reservePrivateNameInNestedScopes(fullName);
@@ -142052,7 +142254,7 @@ ${lanes.join("\n")}
142052
142254
  ""
142053
142255
  );
142054
142256
  }
142055
- function generateNameForMethodOrAccessor(node, privateName, prefix2, suffix) {
142257
+ function generateNameForMethodOrAccessor(node, privateName, prefix, suffix) {
142056
142258
  if (isIdentifier2(node.name)) {
142057
142259
  return generateNameCached(node.name, privateName);
142058
142260
  }
@@ -142061,11 +142263,11 @@ ${lanes.join("\n")}
142061
142263
  /*reservedInNestedScopes*/
142062
142264
  false,
142063
142265
  privateName,
142064
- prefix2,
142266
+ prefix,
142065
142267
  suffix
142066
142268
  );
142067
142269
  }
142068
- function generateNameForNode(node, privateName, flags, prefix2, suffix) {
142270
+ function generateNameForNode(node, privateName, flags, prefix, suffix) {
142069
142271
  switch (node.kind) {
142070
142272
  case 80:
142071
142273
  case 81:
@@ -142075,20 +142277,20 @@ ${lanes.join("\n")}
142075
142277
  !!(flags & 16),
142076
142278
  !!(flags & 8),
142077
142279
  privateName,
142078
- prefix2,
142280
+ prefix,
142079
142281
  suffix
142080
142282
  );
142081
142283
  case 267:
142082
142284
  case 266:
142083
- Debug.assert(!prefix2 && !suffix && !privateName);
142285
+ Debug.assert(!prefix && !suffix && !privateName);
142084
142286
  return generateNameForModuleOrEnum(node);
142085
142287
  case 272:
142086
142288
  case 278:
142087
- Debug.assert(!prefix2 && !suffix && !privateName);
142289
+ Debug.assert(!prefix && !suffix && !privateName);
142088
142290
  return generateNameForImportOrExportDeclaration(node);
142089
142291
  case 262:
142090
142292
  case 263: {
142091
- Debug.assert(!prefix2 && !suffix && !privateName);
142293
+ Debug.assert(!prefix && !suffix && !privateName);
142092
142294
  const name = node.name;
142093
142295
  if (name && !isGeneratedIdentifier(name)) {
142094
142296
  return generateNameForNode(
@@ -142096,29 +142298,29 @@ ${lanes.join("\n")}
142096
142298
  /*privateName*/
142097
142299
  false,
142098
142300
  flags,
142099
- prefix2,
142301
+ prefix,
142100
142302
  suffix
142101
142303
  );
142102
142304
  }
142103
142305
  return generateNameForExportDefault();
142104
142306
  }
142105
142307
  case 277:
142106
- Debug.assert(!prefix2 && !suffix && !privateName);
142308
+ Debug.assert(!prefix && !suffix && !privateName);
142107
142309
  return generateNameForExportDefault();
142108
142310
  case 231:
142109
- Debug.assert(!prefix2 && !suffix && !privateName);
142311
+ Debug.assert(!prefix && !suffix && !privateName);
142110
142312
  return generateNameForClassExpression();
142111
142313
  case 174:
142112
142314
  case 177:
142113
142315
  case 178:
142114
- return generateNameForMethodOrAccessor(node, privateName, prefix2, suffix);
142316
+ return generateNameForMethodOrAccessor(node, privateName, prefix, suffix);
142115
142317
  case 167:
142116
142318
  return makeTempVariableName(
142117
142319
  0,
142118
142320
  /*reservedInNestedScopes*/
142119
142321
  true,
142120
142322
  privateName,
142121
- prefix2,
142323
+ prefix,
142122
142324
  suffix
142123
142325
  );
142124
142326
  default:
@@ -142127,18 +142329,18 @@ ${lanes.join("\n")}
142127
142329
  /*reservedInNestedScopes*/
142128
142330
  false,
142129
142331
  privateName,
142130
- prefix2,
142332
+ prefix,
142131
142333
  suffix
142132
142334
  );
142133
142335
  }
142134
142336
  }
142135
142337
  function makeName(name) {
142136
142338
  const autoGenerate = name.emitNode.autoGenerate;
142137
- const prefix2 = formatGeneratedNamePart(autoGenerate.prefix, generateName);
142339
+ const prefix = formatGeneratedNamePart(autoGenerate.prefix, generateName);
142138
142340
  const suffix = formatGeneratedNamePart(autoGenerate.suffix);
142139
142341
  switch (autoGenerate.flags & 7) {
142140
142342
  case 1:
142141
- return makeTempVariableName(0, !!(autoGenerate.flags & 8), isPrivateIdentifier(name), prefix2, suffix);
142343
+ return makeTempVariableName(0, !!(autoGenerate.flags & 8), isPrivateIdentifier(name), prefix, suffix);
142142
142344
  case 2:
142143
142345
  Debug.assertNode(name, isIdentifier2);
142144
142346
  return makeTempVariableName(
@@ -142146,7 +142348,7 @@ ${lanes.join("\n")}
142146
142348
  !!(autoGenerate.flags & 8),
142147
142349
  /*privateName*/
142148
142350
  false,
142149
- prefix2,
142351
+ prefix,
142150
142352
  suffix
142151
142353
  );
142152
142354
  case 3:
@@ -142156,7 +142358,7 @@ ${lanes.join("\n")}
142156
142358
  !!(autoGenerate.flags & 16),
142157
142359
  !!(autoGenerate.flags & 8),
142158
142360
  isPrivateIdentifier(name),
142159
- prefix2,
142361
+ prefix,
142160
142362
  suffix
142161
142363
  );
142162
142364
  }
@@ -158712,8 +158914,8 @@ ${lanes.join("\n")}
158712
158914
  }
158713
158915
  function buildLinkParts(link, checker) {
158714
158916
  var _a;
158715
- const prefix2 = isJSDocLink(link) ? "link" : isJSDocLinkCode(link) ? "linkcode" : "linkplain";
158716
- const parts = [linkPart(`{@${prefix2} `)];
158917
+ const prefix = isJSDocLink(link) ? "link" : isJSDocLinkCode(link) ? "linkcode" : "linkplain";
158918
+ const parts = [linkPart(`{@${prefix} `)];
158717
158919
  if (!link.name) {
158718
158920
  if (link.text) {
158719
158921
  parts.push(linkTextPart(link.text));
@@ -160186,9 +160388,9 @@ ${lanes.join("\n")}
160186
160388
  let token = 0;
160187
160389
  let lastNonTriviaToken = 0;
160188
160390
  const templateStack = [];
160189
- const { prefix: prefix2, pushTemplate } = getPrefixFromLexState(lexState);
160190
- text = prefix2 + text;
160191
- const offset = prefix2.length;
160391
+ const { prefix, pushTemplate } = getPrefixFromLexState(lexState);
160392
+ text = prefix + text;
160393
+ const offset = prefix.length;
160192
160394
  if (pushTemplate) {
160193
160395
  templateStack.push(
160194
160396
  16
@@ -169346,11 +169548,11 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
169346
169548
  if (decls && decls.some((d3) => d3.parent === scopeDecl)) {
169347
169549
  return factory.createIdentifier(symbol.name);
169348
169550
  }
169349
- const prefix2 = tryReplaceWithQualifiedNameOrPropertyAccess(symbol.parent, scopeDecl, isTypeNode2);
169350
- if (prefix2 === void 0) {
169551
+ const prefix = tryReplaceWithQualifiedNameOrPropertyAccess(symbol.parent, scopeDecl, isTypeNode2);
169552
+ if (prefix === void 0) {
169351
169553
  return void 0;
169352
169554
  }
169353
- return isTypeNode2 ? factory.createQualifiedName(prefix2, factory.createIdentifier(symbol.name)) : factory.createPropertyAccessExpression(prefix2, symbol.name);
169555
+ return isTypeNode2 ? factory.createQualifiedName(prefix, factory.createIdentifier(symbol.name)) : factory.createPropertyAccessExpression(prefix, symbol.name);
169354
169556
  }
169355
169557
  }
169356
169558
  function getExtractableParent(node) {
@@ -170665,13 +170867,13 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
170665
170867
  if (textChangeRange) {
170666
170868
  if (version2 !== sourceFile.version) {
170667
170869
  let newText;
170668
- const prefix2 = textChangeRange.span.start !== 0 ? sourceFile.text.substr(0, textChangeRange.span.start) : "";
170870
+ const prefix = textChangeRange.span.start !== 0 ? sourceFile.text.substr(0, textChangeRange.span.start) : "";
170669
170871
  const suffix = textSpanEnd(textChangeRange.span) !== sourceFile.text.length ? sourceFile.text.substr(textSpanEnd(textChangeRange.span)) : "";
170670
170872
  if (textChangeRange.newLength === 0) {
170671
- newText = prefix2 && suffix ? prefix2 + suffix : prefix2 || suffix;
170873
+ newText = prefix && suffix ? prefix + suffix : prefix || suffix;
170672
170874
  } else {
170673
170875
  const changedText = scriptSnapshot.getText(textChangeRange.span.start, textChangeRange.span.start + textChangeRange.newLength);
170674
- newText = prefix2 && suffix ? prefix2 + changedText + suffix : prefix2 ? prefix2 + changedText : changedText + suffix;
170876
+ newText = prefix && suffix ? prefix + changedText + suffix : prefix ? prefix + changedText : changedText + suffix;
170675
170877
  }
170676
170878
  const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks);
170677
170879
  setSourceFileFields(newSourceFile, scriptSnapshot, version2);
@@ -172801,8 +173003,8 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
172801
173003
  const end = pos + 6;
172802
173004
  const typeChecker = program.getTypeChecker();
172803
173005
  const symbol = typeChecker.getSymbolAtLocation(node.parent);
172804
- const prefix2 = symbol ? `${typeChecker.symbolToString(symbol, node.parent)} ` : "";
172805
- return { text: `${prefix2}static {}`, pos, end };
173006
+ const prefix = symbol ? `${typeChecker.symbolToString(symbol, node.parent)} ` : "";
173007
+ return { text: `${prefix}static {}`, pos, end };
172806
173008
  }
172807
173009
  const declName = isAssignedExpression(node) ? node.parent.name : Debug.checkDefined(getNameOfDeclaration(node), "Expected call hierarchy item to have a name");
172808
173010
  let text = isIdentifier2(declName) ? idText(declName) : isStringOrNumericLiteralLike(declName) ? declName.text : isComputedPropertyName(declName) ? isStringOrNumericLiteralLike(declName.expression) ? declName.expression.text : void 0 : void 0;
@@ -176148,18 +176350,18 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
176148
176350
  const commentNode = node.parent;
176149
176351
  const { leftSibling, rightSibling } = getLeftAndRightSiblings(node);
176150
176352
  let pos = commentNode.getStart();
176151
- let prefix2 = "";
176353
+ let prefix = "";
176152
176354
  if (!leftSibling && commentNode.comment) {
176153
176355
  pos = findEndOfTextBetween(commentNode, commentNode.getStart(), node.getStart());
176154
- prefix2 = `${newLine} */${newLine}`;
176356
+ prefix = `${newLine} */${newLine}`;
176155
176357
  }
176156
176358
  if (leftSibling) {
176157
176359
  if (fixAll && isJSDocTypedefTag(leftSibling)) {
176158
176360
  pos = node.getStart();
176159
- prefix2 = "";
176361
+ prefix = "";
176160
176362
  } else {
176161
176363
  pos = findEndOfTextBetween(commentNode, leftSibling.getStart(), node.getStart());
176162
- prefix2 = `${newLine} */${newLine}`;
176364
+ prefix = `${newLine} */${newLine}`;
176163
176365
  }
176164
176366
  }
176165
176367
  let end = commentNode.getEnd();
@@ -176173,7 +176375,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
176173
176375
  suffix = `${newLine}/**${newLine} * `;
176174
176376
  }
176175
176377
  }
176176
- changes.replaceRange(sourceFile, { pos, end }, declaration, { prefix: prefix2, suffix });
176378
+ changes.replaceRange(sourceFile, { pos, end }, declaration, { prefix, suffix });
176177
176379
  }
176178
176380
  function getLeftAndRightSiblings(typedefNode) {
176179
176381
  const commentNode = typedefNode.parent;
@@ -180591,9 +180793,9 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
180591
180793
  result.push(createDeleteFix(deletion, [Diagnostics.Remove_unused_declaration_for_Colon_0, name.getText(sourceFile)]));
180592
180794
  }
180593
180795
  }
180594
- const prefix2 = ts_textChanges_exports.ChangeTracker.with(context, (t) => tryPrefixDeclaration(t, errorCode, sourceFile, token));
180595
- if (prefix2.length) {
180596
- result.push(createCodeFixAction(fixName3, prefix2, [Diagnostics.Prefix_0_with_an_underscore, token.getText(sourceFile)], fixIdPrefix, Diagnostics.Prefix_all_unused_declarations_with_where_possible));
180796
+ const prefix = ts_textChanges_exports.ChangeTracker.with(context, (t) => tryPrefixDeclaration(t, errorCode, sourceFile, token));
180797
+ if (prefix.length) {
180798
+ result.push(createCodeFixAction(fixName3, prefix, [Diagnostics.Prefix_0_with_an_underscore, token.getText(sourceFile)], fixIdPrefix, Diagnostics.Prefix_all_unused_declarations_with_where_possible));
180597
180799
  }
180598
180800
  return result;
180599
180801
  },
@@ -190294,9 +190496,9 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
190294
190496
  function getDirectoryMatches(directoryName) {
190295
190497
  return mapDefined(tryGetDirectories(host, directoryName), (dir) => dir === "node_modules" ? void 0 : directoryResult(dir));
190296
190498
  }
190297
- function trimPrefixAndSuffix(path, prefix2) {
190499
+ function trimPrefixAndSuffix(path, prefix) {
190298
190500
  return firstDefined(matchingSuffixes, (suffix) => {
190299
- const inner = withoutStartAndEnd(normalizePath(path), prefix2, suffix);
190501
+ const inner = withoutStartAndEnd(normalizePath(path), prefix, suffix);
190300
190502
  return inner === void 0 ? void 0 : removeLeadingDirectorySeparator(inner);
190301
190503
  });
190302
190504
  }
@@ -190329,7 +190531,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
190329
190531
  if (!match) {
190330
190532
  return void 0;
190331
190533
  }
190332
- const [, prefix2, kind, toComplete] = match;
190534
+ const [, prefix, kind, toComplete] = match;
190333
190535
  const scriptPath = getDirectoryPath(sourceFile.path);
190334
190536
  const names = kind === "path" ? getCompletionEntriesForDirectoryFragment(
190335
190537
  toComplete,
@@ -190342,7 +190544,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
190342
190544
  true,
190343
190545
  sourceFile.path
190344
190546
  ) : kind === "types" ? getCompletionEntriesFromTypings(program, host, moduleSpecifierResolutionHost, scriptPath, getFragmentDirectory(toComplete), getExtensionOptions(compilerOptions, 1, sourceFile)) : Debug.fail();
190345
- return addReplacementSpans(toComplete, range.pos + prefix2.length, arrayFrom(names.values()));
190547
+ return addReplacementSpans(toComplete, range.pos + prefix.length, arrayFrom(names.values()));
190346
190548
  }
190347
190549
  function getCompletionEntriesFromTypings(program, host, moduleSpecifierResolutionHost, scriptPath, fragmentDirectory, extensionOptions, result = createNameAndKindSet()) {
190348
190550
  const options = program.getCompilerOptions();
@@ -196693,8 +196895,8 @@ ${content}
196693
196895
  ), spacePart()];
196694
196896
  function getSignatureHelpItem(candidateSignature, callTargetDisplayParts, isTypeParameterList, checker, enclosingDeclaration, sourceFile) {
196695
196897
  const infos = (isTypeParameterList ? itemInfoForTypeParameters : itemInfoForParameters)(candidateSignature, checker, enclosingDeclaration, sourceFile);
196696
- return map(infos, ({ isVariadic, parameters, prefix: prefix2, suffix }) => {
196697
- const prefixDisplayParts = [...callTargetDisplayParts, ...prefix2];
196898
+ return map(infos, ({ isVariadic, parameters, prefix, suffix }) => {
196899
+ const prefixDisplayParts = [...callTargetDisplayParts, ...prefix];
196698
196900
  const suffixDisplayParts = [...suffix, ...returnTypeToDisplayParts(candidateSignature, enclosingDeclaration, checker)];
196699
196901
  const documentation = candidateSignature.getDocumentationComment(checker);
196700
196902
  const tags = candidateSignature.getJsDocTags();
@@ -198781,11 +198983,11 @@ ${options.prefix}` : "\n" : options.prefix
198781
198983
  const noIndent = options.indentation !== void 0 || getLineStartPositionForPosition(pos, targetSourceFile) === pos ? text : text.replace(/^\s+/, "");
198782
198984
  return (options.prefix || "") + noIndent + (!options.suffix || endsWith(noIndent, options.suffix) ? "" : options.suffix);
198783
198985
  }
198784
- function getFormattedTextOfNode(nodeIn, targetSourceFile, sourceFile, pos, { indentation, prefix: prefix2, delta }, newLineCharacter, formatContext, validate) {
198986
+ function getFormattedTextOfNode(nodeIn, targetSourceFile, sourceFile, pos, { indentation, prefix, delta }, newLineCharacter, formatContext, validate) {
198785
198987
  const { node, text } = getNonformattedText(nodeIn, targetSourceFile, newLineCharacter);
198786
198988
  if (validate) validate(node, text);
198787
198989
  const formatOptions = getFormatCodeSettingsForWriting(formatContext, targetSourceFile);
198788
- const initialIndentation = indentation !== void 0 ? indentation : ts_formatting_exports.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix2 === newLineCharacter || getLineStartPositionForPosition(pos, targetSourceFile) === pos);
198990
+ const initialIndentation = indentation !== void 0 ? indentation : ts_formatting_exports.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix === newLineCharacter || getLineStartPositionForPosition(pos, targetSourceFile) === pos);
198789
198991
  if (delta === void 0) {
198790
198992
  delta = ts_formatting_exports.SmartIndenter.shouldIndentChildNode(formatOptions, nodeIn) ? formatOptions.indentSize || 0 : 0;
198791
198993
  }
@@ -216510,9 +216712,9 @@ Project '${project.projectName}' (${ProjectKind[project.projectKind]}) ${counter
216510
216712
  );
216511
216713
  if (completions === void 0) return void 0;
216512
216714
  if (kind === "completions-full") return completions;
216513
- const prefix2 = args.prefix || "";
216715
+ const prefix = args.prefix || "";
216514
216716
  const entries = mapDefined(completions.entries, (entry) => {
216515
- if (completions.isMemberCompletion || startsWith(entry.name.toLowerCase(), prefix2.toLowerCase())) {
216717
+ if (completions.isMemberCompletion || startsWith(entry.name.toLowerCase(), prefix.toLowerCase())) {
216516
216718
  const convertedSpan = entry.replacementSpan ? toProtocolTextSpan(entry.replacementSpan, scriptInfo) : void 0;
216517
216719
  return {
216518
216720
  ...entry,
@@ -221457,15 +221659,15 @@ var require_to_regex_range = __commonJS({
221457
221659
  }
221458
221660
  return tokens;
221459
221661
  }
221460
- function filterPatterns(arr, comparison, prefix2, intersection, options) {
221662
+ function filterPatterns(arr, comparison, prefix, intersection, options) {
221461
221663
  let result = [];
221462
221664
  for (let ele of arr) {
221463
221665
  let { string } = ele;
221464
221666
  if (!intersection && !contains(comparison, "string", string)) {
221465
- result.push(prefix2 + string);
221667
+ result.push(prefix + string);
221466
221668
  }
221467
221669
  if (intersection && contains(comparison, "string", string)) {
221468
- result.push(prefix2 + string);
221670
+ result.push(prefix + string);
221469
221671
  }
221470
221672
  }
221471
221673
  return result;
@@ -221576,7 +221778,7 @@ var require_fill_range = __commonJS({
221576
221778
  var toSequence = (parts, options, maxLen) => {
221577
221779
  parts.negatives.sort((a, b4) => a < b4 ? -1 : a > b4 ? 1 : 0);
221578
221780
  parts.positives.sort((a, b4) => a < b4 ? -1 : a > b4 ? 1 : 0);
221579
- let prefix2 = options.capture ? "" : "?:";
221781
+ let prefix = options.capture ? "" : "?:";
221580
221782
  let positives = "";
221581
221783
  let negatives = "";
221582
221784
  let result;
@@ -221584,7 +221786,7 @@ var require_fill_range = __commonJS({
221584
221786
  positives = parts.positives.map((v2) => toMaxLen(String(v2), maxLen)).join("|");
221585
221787
  }
221586
221788
  if (parts.negatives.length) {
221587
- negatives = `-(${prefix2}${parts.negatives.map((v2) => toMaxLen(String(v2), maxLen)).join("|")})`;
221789
+ negatives = `-(${prefix}${parts.negatives.map((v2) => toMaxLen(String(v2), maxLen)).join("|")})`;
221588
221790
  }
221589
221791
  if (positives && negatives) {
221590
221792
  result = `${positives}|${negatives}`;
@@ -221592,7 +221794,7 @@ var require_fill_range = __commonJS({
221592
221794
  result = positives || negatives;
221593
221795
  }
221594
221796
  if (options.wrap) {
221595
- return `(${prefix2}${result})`;
221797
+ return `(${prefix}${result})`;
221596
221798
  }
221597
221799
  return result;
221598
221800
  };
@@ -221608,8 +221810,8 @@ var require_fill_range = __commonJS({
221608
221810
  var toRegex = (start, end, options) => {
221609
221811
  if (Array.isArray(start)) {
221610
221812
  let wrap = options.wrap === true;
221611
- let prefix2 = options.capture ? "" : "?:";
221612
- return wrap ? `(${prefix2}${start.join("|")})` : start.join("|");
221813
+ let prefix = options.capture ? "" : "?:";
221814
+ return wrap ? `(${prefix}${start.join("|")})` : start.join("|");
221613
221815
  }
221614
221816
  return toRegexRange(start, end, options);
221615
221817
  };
@@ -221731,20 +221933,20 @@ var require_compile = __commonJS({
221731
221933
  const invalidBlock = utils.isInvalidBrace(parent);
221732
221934
  const invalidNode = node.invalid === true && options.escapeInvalid === true;
221733
221935
  const invalid = invalidBlock === true || invalidNode === true;
221734
- const prefix2 = options.escapeInvalid === true ? "\\" : "";
221936
+ const prefix = options.escapeInvalid === true ? "\\" : "";
221735
221937
  let output = "";
221736
221938
  if (node.isOpen === true) {
221737
- return prefix2 + node.value;
221939
+ return prefix + node.value;
221738
221940
  }
221739
221941
  if (node.isClose === true) {
221740
- console.log("node.isClose", prefix2, node.value);
221741
- return prefix2 + node.value;
221942
+ console.log("node.isClose", prefix, node.value);
221943
+ return prefix + node.value;
221742
221944
  }
221743
221945
  if (node.type === "open") {
221744
- return invalid ? prefix2 + node.value : "(";
221946
+ return invalid ? prefix + node.value : "(";
221745
221947
  }
221746
221948
  if (node.type === "close") {
221747
- return invalid ? prefix2 + node.value : ")";
221949
+ return invalid ? prefix + node.value : ")";
221748
221950
  }
221749
221951
  if (node.type === "comma") {
221750
221952
  return node.prev.type === "comma" ? "" : invalid ? node.value : "|";
@@ -222780,10 +222982,10 @@ var require_scan = __commonJS({
222780
222982
  isGlob = false;
222781
222983
  }
222782
222984
  let base = str;
222783
- let prefix2 = "";
222985
+ let prefix = "";
222784
222986
  let glob = "";
222785
222987
  if (start > 0) {
222786
- prefix2 = str.slice(0, start);
222988
+ prefix = str.slice(0, start);
222787
222989
  str = str.slice(start);
222788
222990
  lastIndex -= start;
222789
222991
  }
@@ -222808,7 +223010,7 @@ var require_scan = __commonJS({
222808
223010
  }
222809
223011
  }
222810
223012
  const state = {
222811
- prefix: prefix2,
223013
+ prefix,
222812
223014
  input,
222813
223015
  start,
222814
223016
  base,
@@ -222837,7 +223039,7 @@ var require_scan = __commonJS({
222837
223039
  if (opts.tokens) {
222838
223040
  if (idx === 0 && start !== 0) {
222839
223041
  tokens[idx].isPrefix = true;
222840
- tokens[idx].value = prefix2;
223042
+ tokens[idx].value = prefix;
222841
223043
  } else {
222842
223044
  tokens[idx].value = value;
222843
223045
  }
@@ -227055,8 +227257,8 @@ ${nodeLocation}` : message2;
227055
227257
  errors.ArgumentTypeError = ArgumentTypeError;
227056
227258
  class PathNotFoundError extends BaseError {
227057
227259
  path;
227058
- constructor(path2, prefix2 = "Path") {
227059
- super(`${prefix2} not found: ${path2}`);
227260
+ constructor(path2, prefix = "Path") {
227261
+ super(`${prefix} not found: ${path2}`);
227060
227262
  this.path = path2;
227061
227263
  }
227062
227264
  code = "ENOENT";
@@ -282702,7 +282904,7 @@ Node text: ${this.#forgottenText}`;
282702
282904
  // src/index.ts
282703
282905
  init_cjs_shims();
282704
282906
  init_dist2();
282705
- var import_picocolors27 = __toESM(require_picocolors(), 1);
282907
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
282706
282908
  var import_fs13 = require("fs");
282707
282909
  var import_path8 = require("path");
282708
282910
  var import_child_process3 = require("child_process");
@@ -283374,32 +283576,26 @@ function parseDevArgs(argv) {
283374
283576
  url,
283375
283577
  apiKey: process.env.ABLO_API_KEY,
283376
283578
  watch: watchEnabled,
283377
- planeLabel: "sandbox"
283579
+ planeLabel: "branch"
283378
283580
  };
283379
283581
  }
283380
283582
  function classifyKey(apiKey) {
283381
283583
  if (!apiKey) {
283382
283584
  return {
283383
283585
  ok: false,
283384
- reason: `No API key. Run ${import_picocolors15.default.bold("npx ablo login")}, or set ${import_picocolors15.default.bold("ABLO_API_KEY")}: ${import_picocolors15.default.bold("sk_test_")} runs the ${import_picocolors15.default.bold("npx ablo dev")} watch loop, ${import_picocolors15.default.bold("sk_live_")} deploys production with ${import_picocolors15.default.bold("npx ablo push")}.`
283385
- };
283386
- }
283387
- if (apiKey.startsWith("sk_test_")) return { ok: true };
283388
- if (apiKey.startsWith("sk_live_")) {
283389
- return {
283390
- ok: false,
283391
- reason: `A ${import_picocolors15.default.bold("sk_live_")} key deploys production schema in one reviewed step: ${import_picocolors15.default.bold("npx ablo push")}. The ${import_picocolors15.default.bold("--watch")} loop runs on a ${import_picocolors15.default.bold("sk_test_")} key, which reaches the same API and the same schema over its own rows.`
283586
+ reason: `No API key. Run ${import_picocolors15.default.bold("npx ablo login")}, or set a branch-bound ${import_picocolors15.default.bold("ABLO_API_KEY")}. ${import_picocolors15.default.bold("npx ablo dev")} prepares a development branch.`
283392
283587
  };
283393
283588
  }
283589
+ if ((0, import_credentialPolicy2.classifyCredentialKind)(apiKey) === "secret") return { ok: true };
283394
283590
  if ((0, import_credentialPolicy2.classifyCredentialKind)(apiKey) === "restricted") {
283395
283591
  return {
283396
283592
  ok: false,
283397
- reason: `Authoring schema needs a secret key. ${import_picocolors15.default.bold("sk_test_")} runs the ${import_picocolors15.default.bold("npx ablo dev")} watch loop, ${import_picocolors15.default.bold("sk_live_")} deploys production with ${import_picocolors15.default.bold("npx ablo push")}. A restricted ${import_picocolors15.default.bold("rk_")} key carries only the scopes it was minted with.`
283593
+ reason: `Authoring schema needs a branch-bound secret ${import_picocolors15.default.bold("sk_")} key. ${import_picocolors15.default.bold("npx ablo dev")} prepares a development branch. A restricted ${import_picocolors15.default.bold("rk_")} key carries only the scopes it was minted with.`
283398
283594
  };
283399
283595
  }
283400
283596
  return {
283401
283597
  ok: false,
283402
- reason: `${import_picocolors15.default.bold("ABLO_API_KEY")} is not an Ablo key. Expected ${import_picocolors15.default.bold("sk_test_\u2026")} for the ${import_picocolors15.default.bold("npx ablo dev")} watch loop, or ${import_picocolors15.default.bold("sk_live_\u2026")} for a production deploy with ${import_picocolors15.default.bold("npx ablo push")}.`
283598
+ reason: `${import_picocolors15.default.bold("ABLO_API_KEY")} is not a secret Ablo key. Expected a branch-bound ${import_picocolors15.default.bold("sk_\u2026")} credential. Run ${import_picocolors15.default.bold("npx ablo dev")} to prepare a development branch.`
283403
283599
  };
283404
283600
  }
283405
283601
  function wireEnvLocal(apiKey, cwd = process.cwd()) {
@@ -283494,7 +283690,7 @@ async function runPush(schema, args) {
283494
283690
  }
283495
283691
  if (status2 === 403) {
283496
283692
  const serverSays = body.message ?? body.reason;
283497
- const hint = body.code === "database_role_cannot_enforce_rls" ? `Run ${import_picocolors15.default.bold("npx ablo migrate")} \u2014 it creates the scoped role for you (your DB credential never leaves this machine).` : `Schema authoring needs a ${import_picocolors15.default.bold("sandbox")} key with ${import_picocolors15.default.bold("schema:push")} \u2014 manage keys at ${import_picocolors15.default.cyan("https://abloatai.com")}.`;
283693
+ const hint = body.code === "database_role_cannot_enforce_rls" ? `Run ${import_picocolors15.default.bold("npx ablo migrate")} \u2014 it creates the scoped role for you (your DB credential never leaves this machine).` : `Schema authoring needs a branch-bound ${import_picocolors15.default.bold("sk_")} key with ${import_picocolors15.default.bold("schema:push")} \u2014 manage keys at ${import_picocolors15.default.cyan("https://abloatai.com")}.`;
283498
283694
  return {
283499
283695
  ok: false,
283500
283696
  message: `${serverSays ?? "This key can't author schema (missing schema:push scope)."}
@@ -283520,7 +283716,7 @@ async function dev(argv, runtime = {}) {
283520
283716
  process.exit(1);
283521
283717
  }
283522
283718
  if (runtime.apiKey) args.apiKey = runtime.apiKey;
283523
- else if (!args.apiKey) args.apiKey = resolveEffectiveApiKey("sandbox").key;
283719
+ else if (!args.apiKey) args.apiKey = resolveRuntimeApiKey("sandbox").key;
283524
283720
  if (runtime.branch) args.planeLabel = runtime.branch.slug;
283525
283721
  const key = classifyKey(args.apiKey);
283526
283722
  if (!key.ok) {
@@ -283549,7 +283745,7 @@ async function dev(argv, runtime = {}) {
283549
283745
  console.log(` ${import_picocolors15.default.dim("api")} ${args.url}
283550
283746
  `);
283551
283747
  const s = Y2();
283552
- s.start("Pushing schema definition (sandbox)");
283748
+ s.start("Pushing schema definition (development branch)");
283553
283749
  const first = await runPush(schema, args);
283554
283750
  s.stop(first.message, first.ok ? 0 : 1);
283555
283751
  if (!first.ok) process.exit(1);
@@ -283574,7 +283770,7 @@ async function dev(argv, runtime = {}) {
283574
283770
  ${import_picocolors15.default.green("\u2713")} ${wireEnvLocal(args.apiKey)}`);
283575
283771
  console.log(` ${import_picocolors15.default.dim("Frameworks load it automatically; plain Node: node --env-file=.env.local app.ts")}`);
283576
283772
  }
283577
- console.log(` Your app is wired for ${runtime.branch ? `branch ${runtime.branch.slug}` : "the sandbox"}.`);
283773
+ console.log(` Your app is wired for ${runtime.branch ? `branch ${runtime.branch.slug}` : "this branch"}.`);
283578
283774
  if (!args.watch) return;
283579
283775
  const abs = (0, import_path5.resolve)(process.cwd(), args.schemaPath);
283580
283776
  console.log(` ${import_picocolors15.default.dim(`watching ${args.schemaPath} \u2026 (Ctrl-C to stop)`)}
@@ -283736,7 +283932,226 @@ async function runBranchDev(argv, dependencies = {}) {
283736
283932
  });
283737
283933
  }
283738
283934
 
283935
+ // src/whoami.ts
283936
+ init_cjs_shims();
283937
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
283938
+ var import_errors13 = require("@abloatai/transaction/errors");
283939
+ init_config();
283940
+ init_controlPlane();
283941
+
283942
+ // src/credentialCapability.ts
283943
+ init_cjs_shims();
283944
+ var import_credentialPolicy3 = require("@abloatai/transaction/auth/credentialPolicy");
283945
+ function secretCounterpart(key) {
283946
+ void key;
283947
+ return "sk_";
283948
+ }
283949
+ function credentialCapability(key) {
283950
+ const kind = key ? (0, import_credentialPolicy3.classifyCredentialKind)(key) : null;
283951
+ if (!key || kind === null) return { kind, label: "", note: null };
283952
+ const secret = `${secretCounterpart(key)}\u2026`;
283953
+ switch (kind) {
283954
+ case "secret":
283955
+ return { kind, label: "", note: null };
283956
+ case "restricted":
283957
+ return {
283958
+ kind,
283959
+ label: "scoped",
283960
+ note: `A scoped key does exactly what it was minted for. Authoring schema requires a branch-bound secret ${secret} key with schema:push.`
283961
+ };
283962
+ case "publishable":
283963
+ return {
283964
+ kind,
283965
+ label: "read-only",
283966
+ note: `This is the key that is safe to ship in a browser bundle, and it reads. Work from a terminal wants a secret ${secret} key.`
283967
+ };
283968
+ case "ephemeral":
283969
+ return {
283970
+ kind,
283971
+ label: "session key",
283972
+ note: `This is a short-lived credential minted for one signed-in person, and it expires. Pushing a schema needs a secret ${secret} key.`
283973
+ };
283974
+ }
283975
+ }
283976
+
283977
+ // src/whoami.ts
283978
+ init_dbRole();
283979
+ init_theme();
283980
+ init_target();
283981
+ var WHOAMI_USAGE = ` ablo whoami \u2014 show the plane a credential acts on
283982
+
283983
+ Usage
283984
+ npx ablo whoami
283985
+ npx ablo whoami --key-env <NAME>
283986
+ npx ablo whoami --key <VALUE>
283987
+ npx ablo whoami --json
283988
+
283989
+ Credential choice
283990
+ With no flag, uses ABLO_API_KEY, then the active project's stored data key,
283991
+ then the stored login. This command does not load .env files implicitly.
283992
+
283993
+ --key-env reads a named variable from the process, .env.local, or .env
283994
+ without putting its value in shell history or the process list. Prefer it
283995
+ for comparing several keys.
283996
+ --key accepts a value directly for one-off use.
283997
+
283998
+ Output never prints the full credential. Identity is confirmed by the server;
283999
+ an invalid key, unreachable server, or unsupported server fails non-zero.`;
284000
+ function parseWhoamiArgs(argv) {
284001
+ let json = false;
284002
+ let key;
284003
+ let keyEnv;
284004
+ for (let i = 0; i < argv.length; i++) {
284005
+ const arg = argv[i];
284006
+ switch (arg) {
284007
+ case "--json":
284008
+ json = true;
284009
+ break;
284010
+ case "--key": {
284011
+ const value = argv[++i];
284012
+ if (!value || value.startsWith("--")) {
284013
+ throw new import_errors13.AbloValidationError("`--key` needs a credential value.", {
284014
+ code: "cli_invalid_arguments"
284015
+ });
284016
+ }
284017
+ key = value;
284018
+ break;
284019
+ }
284020
+ case "--key-env": {
284021
+ const value = argv[++i];
284022
+ if (!value || value.startsWith("--")) {
284023
+ throw new import_errors13.AbloValidationError("`--key-env` needs an environment variable name.", {
284024
+ code: "cli_invalid_arguments"
284025
+ });
284026
+ }
284027
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
284028
+ throw new import_errors13.AbloValidationError(
284029
+ `\`${value}\` is not a valid environment variable name.`,
284030
+ { code: "cli_invalid_arguments" }
284031
+ );
284032
+ }
284033
+ keyEnv = value;
284034
+ break;
284035
+ }
284036
+ default:
284037
+ throw new import_errors13.AbloValidationError(`unknown whoami flag: ${arg}`, {
284038
+ code: "cli_invalid_arguments"
284039
+ });
284040
+ }
284041
+ }
284042
+ if (key && keyEnv) {
284043
+ throw new import_errors13.AbloValidationError("Choose one credential source: `--key` or `--key-env`.", {
284044
+ code: "cli_invalid_arguments"
284045
+ });
284046
+ }
284047
+ return { json, ...key ? { key } : {}, ...keyEnv ? { keyEnv } : {} };
284048
+ }
284049
+ function selectWhoamiCredential(args, cwd = process.cwd()) {
284050
+ if (args.key) return { key: args.key, source: "--key", targetSource: "env" };
284051
+ if (args.keyEnv) {
284052
+ const found = readProjectEnvVariable(args.keyEnv, cwd);
284053
+ if (!found) {
284054
+ throw new import_errors13.AbloAuthenticationError(
284055
+ `${args.keyEnv} is not set in the process environment, .env.local, or .env.`,
284056
+ { code: "cli_api_key_missing" }
284057
+ );
284058
+ }
284059
+ return {
284060
+ key: found.value,
284061
+ source: `${found.source}:${args.keyEnv}`,
284062
+ targetSource: "env"
284063
+ };
284064
+ }
284065
+ const runtimeKey = resolveRuntimeApiKey();
284066
+ const dataKey = runtimeKey.key;
284067
+ if (dataKey) {
284068
+ return {
284069
+ key: dataKey,
284070
+ source: runtimeKey.source === "stored" ? "stored data key" : `${runtimeKey.source ?? "env"}:ABLO_API_KEY`,
284071
+ targetSource: runtimeKey.source ?? "stored"
284072
+ };
284073
+ }
284074
+ const managementKey = resolveManagementKey();
284075
+ if (managementKey) {
284076
+ return {
284077
+ key: managementKey,
284078
+ source: process.env.ABLO_MANAGEMENT_KEY ? "env:ABLO_MANAGEMENT_KEY" : "stored login",
284079
+ targetSource: process.env.ABLO_MANAGEMENT_KEY ? "env" : "stored"
284080
+ };
284081
+ }
284082
+ const ambient = ambientEnvKeyNote();
284083
+ throw new import_errors13.AbloAuthenticationError(
284084
+ `No credential found. Run \`ablo login\`, set ABLO_API_KEY, or pass \`--key-env <NAME>\`.${ambient ? `
284085
+
284086
+ ${ambient}` : ""}`,
284087
+ { code: "cli_api_key_missing" }
284088
+ );
284089
+ }
284090
+ async function whoami(argv) {
284091
+ const args = parseWhoamiArgs(argv);
284092
+ const selected = selectWhoamiCredential(args);
284093
+ const target = await resolveTarget({
284094
+ url: apiBaseUrl(),
284095
+ apiKey: selected.key,
284096
+ keySource: selected.targetSource,
284097
+ strict: true
284098
+ });
284099
+ const confirmed = target.confirmed;
284100
+ if (!confirmed) {
284101
+ throw new import_errors13.AbloAuthenticationError(
284102
+ "The server did not confirm an identity for this credential.",
284103
+ { code: "identity_resolve_failed" }
284104
+ );
284105
+ }
284106
+ const project = confirmed.project;
284107
+ const projectLabel = project ? project.isDefault ? "default" : project.slug : "default";
284108
+ const actsOn = confirmed.branchId ? confirmed.branchRoot ? "production root" : `branch ${confirmed.branchId}` : confirmed.environment ?? "unknown";
284109
+ const capability = credentialCapability(selected.key);
284110
+ if (args.json) {
284111
+ console.log(
284112
+ JSON.stringify(
284113
+ {
284114
+ authenticated: true,
284115
+ key: {
284116
+ prefix: `${selected.key.slice(0, 12)}\u2026`,
284117
+ source: selected.source,
284118
+ kind: capability.kind
284119
+ },
284120
+ organizationId: confirmed.organizationId,
284121
+ project: project ? {
284122
+ id: project.id,
284123
+ slug: projectLabel,
284124
+ name: project.name,
284125
+ default: project.isDefault
284126
+ } : null,
284127
+ environment: confirmed.environment,
284128
+ branchId: confirmed.branchId ?? null,
284129
+ branchRoot: confirmed.branchRoot ?? false
284130
+ },
284131
+ null,
284132
+ 2
284133
+ )
284134
+ );
284135
+ return;
284136
+ }
284137
+ console.log(`
284138
+ ${brand("ablo")} ${import_picocolors16.default.dim("whoami")}
284139
+ `);
284140
+ console.log(
284141
+ ` ${import_picocolors16.default.dim("key")} ${selected.key.slice(0, 12)}\u2026 ${import_picocolors16.default.dim(`(${selected.source} \xB7 ${capability.label})`)}`
284142
+ );
284143
+ console.log(` ${import_picocolors16.default.dim("org")} ${import_picocolors16.default.dim(confirmed.organizationId)}`);
284144
+ console.log(
284145
+ ` ${import_picocolors16.default.dim("project")} ${import_picocolors16.default.bold(projectLabel)}${project ? ` ${import_picocolors16.default.dim(`(${project.id})`)}` : ""}`
284146
+ );
284147
+ console.log(` ${import_picocolors16.default.dim("acts on")} ${import_picocolors16.default.bold(actsOn)}`);
284148
+ console.log(`
284149
+ ${import_picocolors16.default.green("\u2713")} ${import_picocolors16.default.dim("credential accepted; target confirmed by the server")}
284150
+ `);
284151
+ }
284152
+
283739
284153
  // src/commands.ts
284154
+ init_push();
283740
284155
  var CORE_GROUPS = ["Start", "Every day", "More"];
283741
284156
  var FULL_GROUPS = [
283742
284157
  "Set up",
@@ -283786,6 +284201,7 @@ var COMMANDS = [
283786
284201
  { run: "connect apply", does: "Run that setup for you, from a one-time admin URL" },
283787
284202
  { run: "connect check", does: "Confirm your database is ready to share changes with Ablo" },
283788
284203
  { run: "connect scan", does: "List anything Ablo ever set up in your database (read-only)" },
284204
+ { run: "connect locate", does: "See which plane holds a database before connecting it" },
283789
284205
  { run: "connect deregister", does: "Disconnect this project's database \u2014 Ablo stops reading and writing it" }
283790
284206
  ]
283791
284207
  }
@@ -283834,6 +284250,7 @@ var COMMANDS = [
283834
284250
  },
283835
284251
  {
283836
284252
  name: "push",
284253
+ usage: PUSH_USAGE,
283837
284254
  core: { group: "Every day", does: "Upload your schema \u2014 schema only; your rows stay in your database" },
283838
284255
  full: {
283839
284256
  group: "Your schema",
@@ -283875,6 +284292,18 @@ var COMMANDS = [
283875
284292
  ]
283876
284293
  }
283877
284294
  },
284295
+ {
284296
+ name: "whoami",
284297
+ usage: WHOAMI_USAGE,
284298
+ full: {
284299
+ group: "See what's happening",
284300
+ rows: [
284301
+ { run: "whoami", does: "Show the server-confirmed plane the active credential acts on" },
284302
+ { run: "whoami --key-env <NAME>", does: "Inspect another key without exposing it in argv" },
284303
+ { run: "whoami --json", does: "Same, machine-readable" }
284304
+ ]
284305
+ }
284306
+ },
283878
284307
  {
283879
284308
  name: "status",
283880
284309
  core: { group: "Every day", does: "See what this key acts on, your pushed schema, and whether writes will work" },
@@ -283913,7 +284342,7 @@ var COMMANDS = [
283913
284342
  { run: "branch check [id|slug]", does: "CI alias for branch status" },
283914
284343
  { run: "branch create <slug>", does: "Create a child of the production root" },
283915
284344
  { run: "branch ensure <slug> --credential", does: "Resolve a branch and mint its expiring CI key" },
283916
- { run: "branch credential <id>", does: "Mint an expiring branch-bound test key" },
284345
+ { run: "branch credential <id>", does: "Mint an expiring branch-bound runtime key" },
283917
284346
  { run: "branch delete <id>", does: "Delete a non-root branch" }
283918
284347
  ]
283919
284348
  }
@@ -284003,15 +284432,15 @@ function fullRows(group) {
284003
284432
  }
284004
284433
 
284005
284434
  // src/index.ts
284006
- var import_errors20 = require("@abloatai/transaction/errors");
284435
+ var import_errors21 = require("@abloatai/transaction/errors");
284007
284436
  init_push();
284008
284437
 
284009
284438
  // src/generate.ts
284010
284439
  init_cjs_shims();
284011
- var import_errors13 = require("@abloatai/transaction/errors");
284440
+ var import_errors14 = require("@abloatai/transaction/errors");
284012
284441
  var import_fs8 = require("fs");
284013
284442
  var import_path6 = require("path");
284014
- var import_picocolors16 = __toESM(require_picocolors(), 1);
284443
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
284015
284444
  var import_schema7 = require("@abloatai/transaction/schema");
284016
284445
  init_push();
284017
284446
  var DEFAULT_SCHEMA_PATH4 = "ablo/schema.ts";
@@ -284034,7 +284463,7 @@ function parseGenerateArgs(argv) {
284034
284463
  out = argv[++i] ?? out;
284035
284464
  break;
284036
284465
  default:
284037
- throw new import_errors13.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
284466
+ throw new import_errors14.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
284038
284467
  }
284039
284468
  }
284040
284469
  return { schemaPath, exportName, out };
@@ -284044,7 +284473,7 @@ async function generate(argv) {
284044
284473
  try {
284045
284474
  args = parseGenerateArgs(argv);
284046
284475
  } catch (err) {
284047
- console.error(import_picocolors16.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284476
+ console.error(import_picocolors17.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284048
284477
  process.exit(1);
284049
284478
  }
284050
284479
  let source;
@@ -284053,22 +284482,22 @@ async function generate(argv) {
284053
284482
  const schemaJson = JSON.parse((0, import_schema7.serializeSchema)(schema));
284054
284483
  source = (0, import_schema7.generateTypes)(schemaJson);
284055
284484
  } catch (err) {
284056
- console.error(import_picocolors16.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284485
+ console.error(import_picocolors17.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284057
284486
  process.exit(1);
284058
284487
  }
284059
284488
  const abs = (0, import_path6.resolve)(process.cwd(), args.out);
284060
284489
  (0, import_fs8.mkdirSync)((0, import_path6.dirname)(abs), { recursive: true });
284061
284490
  (0, import_fs8.writeFileSync)(abs, source);
284062
- console.log(` ${import_picocolors16.default.green("\u2713")} Generated types \u2192 ${import_picocolors16.default.bold(args.out)}`);
284491
+ console.log(` ${import_picocolors17.default.green("\u2713")} Generated types \u2192 ${import_picocolors17.default.bold(args.out)}`);
284063
284492
  }
284064
284493
 
284065
284494
  // src/login.ts
284066
284495
  init_cjs_shims();
284067
284496
  var import_child_process2 = require("child_process");
284068
- var import_picocolors17 = __toESM(require_picocolors(), 1);
284497
+ var import_picocolors18 = __toESM(require_picocolors(), 1);
284069
284498
  init_dist2();
284070
- var import_errors14 = require("@abloatai/transaction/errors");
284071
- var import_wire7 = require("@abloatai/transaction/wire");
284499
+ var import_errors15 = require("@abloatai/transaction/errors");
284500
+ var import_wire8 = require("@abloatai/transaction/wire");
284072
284501
  init_config();
284073
284502
  init_theme();
284074
284503
  var CLIENT_ID = "ablo-cli";
@@ -284086,20 +284515,21 @@ function openBrowser(url) {
284086
284515
  } catch {
284087
284516
  }
284088
284517
  }
284089
- function parseProjectFlag(argv) {
284090
- const i = argv.indexOf("--project");
284518
+ function parseSlugFlag(argv, flag2) {
284519
+ const i = argv.indexOf(flag2);
284091
284520
  if (i >= 0) {
284092
284521
  const slug = argv[i + 1];
284093
284522
  if (slug && !slug.startsWith("-")) return slug;
284094
284523
  }
284095
- const eq = argv.find((a) => a.startsWith("--project="));
284096
- return eq ? eq.slice("--project=".length) || void 0 : void 0;
284524
+ const eq = argv.find((a) => a.startsWith(`${flag2}=`));
284525
+ return eq ? eq.slice(flag2.length + 1) || void 0 : void 0;
284097
284526
  }
284098
284527
  async function deviceLogin(argv, deps = {}) {
284099
284528
  const openUrl = deps.openUrl ?? openBrowser;
284100
284529
  Ie(`${brand("ablo")} login`);
284101
- const requested = parseProjectFlag(argv) ?? getActiveProject()?.slug;
284530
+ const requested = parseSlugFlag(argv, "--project") ?? getActiveProject()?.slug;
284102
284531
  const targetProject = requested === DEFAULT_PROFILE ? void 0 : requested;
284532
+ const targetOrg = parseSlugFlag(argv, "--org");
284103
284533
  const interactive = Boolean(process.stdout.isTTY && process.stdin.isTTY);
284104
284534
  let account = "login";
284105
284535
  if (interactive) {
@@ -284126,11 +284556,11 @@ async function deviceLogin(argv, deps = {}) {
284126
284556
  process.exit(1);
284127
284557
  }
284128
284558
  const code = await codeRes.json();
284129
- const approvePath = `/cli?user_code=${code.user_code}`;
284559
+ const approvePath = `/cli?user_code=${code.user_code}${targetOrg ? `&org=${encodeURIComponent(targetOrg)}` : ""}`;
284130
284560
  const url = account === "signup" ? `${DASHBOARD_URL}/signup?next=${encodeURIComponent(approvePath)}` : `${DASHBOARD_URL}${approvePath}`;
284131
- Me(`${import_picocolors17.default.bold(code.user_code)}
284561
+ Me(`${import_picocolors18.default.bold(code.user_code)}
284132
284562
 
284133
- ${import_picocolors17.default.dim(url)}`, "Approve in your browser");
284563
+ ${import_picocolors18.default.dim(url)}`, "Approve in your browser");
284134
284564
  openUrl(url);
284135
284565
  const s = Y2();
284136
284566
  s.start("Waiting for approval\u2026");
@@ -284200,7 +284630,7 @@ ${import_picocolors17.default.dim(url)}`, "Approve in your browser");
284200
284630
  }
284201
284631
  if (!provRes.ok) {
284202
284632
  s.stop("Could not provision a key.");
284203
- const err = (0, import_errors14.translateHttpError)(
284633
+ const err = (0, import_errors15.translateHttpError)(
284204
284634
  provRes.status,
284205
284635
  await provRes.json().catch(() => null),
284206
284636
  provRes.headers.get("x-request-id") ?? void 0
@@ -284208,30 +284638,36 @@ ${import_picocolors17.default.dim(url)}`, "Approve in your browser");
284208
284638
  M2.error(err.message);
284209
284639
  if (err.code === "entity_not_found" && targetProject) {
284210
284640
  M2.error(
284211
- `If that isn't the account you meant, run ${import_picocolors17.default.bold("npx ablo logout")} and sign in again. Otherwise create it with ${import_picocolors17.default.bold(`npx ablo projects create ${targetProject}`)}.`
284641
+ `If that isn't the account you meant, run ${import_picocolors18.default.bold("npx ablo logout")} and sign in again. Otherwise create it with ${import_picocolors18.default.bold(`npx ablo projects create ${targetProject}`)}.`
284212
284642
  );
284213
284643
  } else {
284214
284644
  M2.error(
284215
- `The browser approval succeeded but the credential handoff failed. Try ${import_picocolors17.default.bold("npx ablo login")} again.`
284645
+ `The browser approval succeeded but the credential handoff failed. Try ${import_picocolors18.default.bold("npx ablo login")} again.`
284216
284646
  );
284217
284647
  }
284218
284648
  process.exit(1);
284219
284649
  }
284220
- const parsedProv = import_wire7.provisionKeyResponseSchema.safeParse(
284650
+ const parsedProv = import_wire8.provisionKeyResponseSchema.safeParse(
284221
284651
  await provRes.json().catch(() => null)
284222
284652
  );
284223
284653
  if (!parsedProv.success) {
284224
284654
  s.stop("Could not provision a key.");
284225
284655
  M2.error("The key handoff returned something this version does not recognize.");
284226
- M2.error(`Try again, or upgrade with ${import_picocolors17.default.bold("npm i -g @abloatai/ablo")}.`);
284656
+ M2.error(`Try again, or upgrade with ${import_picocolors18.default.bold("npm i -g @abloatai/ablo")}.`);
284227
284657
  process.exit(1);
284228
284658
  }
284229
284659
  const prov = parsedProv.data;
284230
284660
  const entry = (k3) => ({
284231
284661
  apiKey: k3.apiKey,
284232
284662
  ...prov.organizationId ? { organizationId: prov.organizationId } : {},
284663
+ ...prov.organizationSlug ? { organizationSlug: prov.organizationSlug } : {},
284233
284664
  ...k3.expiresAt ? { expiresAt: k3.expiresAt } : {}
284234
284665
  });
284666
+ if (targetOrg && prov.organizationSlug && prov.organizationSlug !== targetOrg) {
284667
+ M2.warn(
284668
+ `The approval chose ${import_picocolors18.default.bold(prov.organizationSlug)}, not ${import_picocolors18.default.bold(targetOrg)}. The credential is scoped to ${import_picocolors18.default.bold(prov.organizationSlug)}.`
284669
+ );
284670
+ }
284235
284671
  const profileName = prov.project?.slug ?? DEFAULT_PROFILE;
284236
284672
  const path = setProfileKeys(
284237
284673
  profileName,
@@ -284241,9 +284677,10 @@ ${import_picocolors17.default.dim(url)}`, "Approve in your browser");
284241
284677
  { mode: "sandbox", activeProject: prov.project ?? void 0 }
284242
284678
  );
284243
284679
  s.stop(`Saved project credential to ${path}`);
284244
- const where = prov.project ? ` ${import_picocolors17.default.dim(`(project ${prov.project.slug})`)}` : "";
284680
+ const orgLabel = prov.organizationSlug ? ` to ${import_picocolors18.default.bold(prov.organizationSlug)}` : "";
284681
+ const where = prov.project ? ` ${import_picocolors18.default.dim(`(project ${prov.project.slug})`)}` : "";
284245
284682
  Se(
284246
- `${import_picocolors17.default.green("\u2713")} Logged in${where}. Run ${import_picocolors17.default.bold("npx ablo dev")} to create or resume your Git branch and start with an expiring runtime key.`
284683
+ `${import_picocolors18.default.green("\u2713")} Logged in${orgLabel}${where}. Run ${import_picocolors18.default.bold("npx ablo dev")} to create or resume your Git branch and start with an expiring runtime key.`
284247
284684
  );
284248
284685
  }
284249
284686
  async function login(argv = [], deps = {}) {
@@ -284252,14 +284689,14 @@ async function login(argv = [], deps = {}) {
284252
284689
  function logout() {
284253
284690
  const removed = clearCredential();
284254
284691
  if (removed) {
284255
- console.log(` ${import_picocolors17.default.green("\u2713")} Logged out ${import_picocolors17.default.dim(`(credentials removed from ${configDir()})`)}`);
284692
+ console.log(` ${import_picocolors18.default.green("\u2713")} Logged out ${import_picocolors18.default.dim(`(credentials removed from ${configDir()})`)}`);
284256
284693
  } else {
284257
- console.log(` ${import_picocolors17.default.dim("\u25CB")} Not logged in \u2014 nothing to remove.`);
284694
+ console.log(` ${import_picocolors18.default.dim("\u25CB")} Not logged in \u2014 nothing to remove.`);
284258
284695
  }
284259
284696
  if (process.env.ABLO_MANAGEMENT_KEY) {
284260
284697
  console.log(
284261
- import_picocolors17.default.dim(
284262
- ` Note: ${import_picocolors17.default.bold("ABLO_MANAGEMENT_KEY")} is still set in this shell and takes precedence.`
284698
+ import_picocolors18.default.dim(
284699
+ ` Note: ${import_picocolors18.default.bold("ABLO_MANAGEMENT_KEY")} is still set in this shell and takes precedence.`
284263
284700
  )
284264
284701
  );
284265
284702
  }
@@ -284271,46 +284708,9 @@ init_projects();
284271
284708
 
284272
284709
  // src/status.ts
284273
284710
  init_cjs_shims();
284274
- var import_picocolors18 = __toESM(require_picocolors(), 1);
284711
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
284275
284712
  init_config();
284276
284713
  init_target();
284277
-
284278
- // src/credentialCapability.ts
284279
- init_cjs_shims();
284280
- var import_credentialPolicy3 = require("@abloatai/transaction/auth/credentialPolicy");
284281
- init_config();
284282
- function secretCounterpart(key) {
284283
- return modeFromKey(key) === "production" ? "sk_live_" : "sk_test_";
284284
- }
284285
- function credentialCapability(key) {
284286
- const kind = key ? (0, import_credentialPolicy3.classifyCredentialKind)(key) : null;
284287
- if (!key || kind === null) return { kind, label: "", note: null };
284288
- const secret = `${secretCounterpart(key)}\u2026`;
284289
- switch (kind) {
284290
- case "secret":
284291
- return { kind, label: "", note: null };
284292
- case "restricted":
284293
- return {
284294
- kind,
284295
- label: "scoped",
284296
- note: `A scoped key does exactly what it was minted for. The production key \`ablo login\` stores is for observing your live plane with \`ablo status\` and \`ablo logs\`; authoring schema there takes a secret ${secret} key from the dashboard.`
284297
- };
284298
- case "publishable":
284299
- return {
284300
- kind,
284301
- label: "read-only",
284302
- note: `This is the key that is safe to ship in a browser bundle, and it reads. Work from a terminal wants a secret ${secret} key.`
284303
- };
284304
- case "ephemeral":
284305
- return {
284306
- kind,
284307
- label: "session key",
284308
- note: `This is a short-lived credential minted for one signed-in person, and it expires. Pushing a schema needs a secret ${secret} key.`
284309
- };
284310
- }
284311
- }
284312
-
284313
- // src/status.ts
284314
284714
  init_theme();
284315
284715
  init_controlPlane();
284316
284716
  var import_schema8 = require("@abloatai/transaction/coordination/schema");
@@ -284318,9 +284718,9 @@ init_readiness();
284318
284718
  function expiryLabel(iso) {
284319
284719
  const ms = Date.parse(iso) - Date.now();
284320
284720
  if (Number.isNaN(ms)) return "";
284321
- if (ms <= 0) return import_picocolors18.default.red("expired");
284721
+ if (ms <= 0) return import_picocolors19.default.red("expired");
284322
284722
  const days = Math.floor(ms / (24 * 60 * 60 * 1e3));
284323
- return import_picocolors18.default.dim(days > 0 ? `expires in ${days}d` : "expires <1d");
284723
+ return import_picocolors19.default.dim(days > 0 ? `expires in ${days}d` : "expires <1d");
284324
284724
  }
284325
284725
  async function ping(apiUrl3) {
284326
284726
  const ctrl = new AbortController();
@@ -284341,55 +284741,55 @@ function formatConflict(conflict) {
284341
284741
  const parts = import_schema8.participantKindSchema.options.flatMap((k3) => conflict[k3] ? [`${k3}:${conflict[k3]}`] : []);
284342
284742
  return parts.length ? `{${parts.join(",")}}` : "";
284343
284743
  }
284344
- function printTargetLines(target, localProject, storedOrganizationId) {
284744
+ function printTargetLines(target, localProject, storedOrganizationId, storedOrganizationSlug) {
284345
284745
  const confirmed = target?.confirmed ?? null;
284346
284746
  const org = confirmed?.organizationId ?? storedOrganizationId;
284347
284747
  if (org) {
284348
- const suffix = confirmed?.organizationId ? "" : ` ${import_picocolors18.default.yellow("(unconfirmed)")}`;
284349
- console.log(` ${import_picocolors18.default.dim("org")} ${import_picocolors18.default.dim(org)}${suffix}`);
284748
+ const suffix = confirmed?.organizationId ? "" : ` ${import_picocolors19.default.yellow("(unconfirmed)")}`;
284749
+ const slug = org === storedOrganizationId ? storedOrganizationSlug : void 0;
284750
+ const label = slug ? `${import_picocolors19.default.bold(slug)} ${import_picocolors19.default.dim(`(${org})`)}` : import_picocolors19.default.dim(org);
284751
+ console.log(` ${import_picocolors19.default.dim("org")} ${label}${suffix}`);
284350
284752
  } else {
284351
284753
  console.log(
284352
- ` ${import_picocolors18.default.dim("org")} ${import_picocolors18.default.yellow("unknown")} ${import_picocolors18.default.dim("(the server did not confirm one for this key)")}`
284754
+ ` ${import_picocolors19.default.dim("org")} ${import_picocolors19.default.yellow("unknown")} ${import_picocolors19.default.dim("(the server did not confirm one for this key)")}`
284353
284755
  );
284354
284756
  }
284355
284757
  let projectLine;
284356
284758
  if (confirmed?.project) {
284357
284759
  const p2 = confirmed.project;
284358
- projectLine = p2.isDefault ? `${import_picocolors18.default.bold("default")} ${import_picocolors18.default.dim("(org-default)")}` : `${import_picocolors18.default.bold(p2.slug)} ${import_picocolors18.default.dim(`(${p2.id})`)}`;
284760
+ projectLine = p2.isDefault ? `${import_picocolors19.default.bold("default")} ${import_picocolors19.default.dim("(org-default)")}` : `${import_picocolors19.default.bold(p2.slug)} ${import_picocolors19.default.dim(`(${p2.id})`)}`;
284359
284761
  } else if (confirmed) {
284360
- projectLine = `${import_picocolors18.default.bold("default")} ${import_picocolors18.default.dim("(org-default)")}`;
284762
+ projectLine = `${import_picocolors19.default.bold("default")} ${import_picocolors19.default.dim("(org-default)")}`;
284361
284763
  } else if (localProject) {
284362
- projectLine = `${import_picocolors18.default.bold(localProject.slug)} ${import_picocolors18.default.dim(`(${localProject.id})`)} ${import_picocolors18.default.yellow("(unconfirmed)")}`;
284764
+ projectLine = `${import_picocolors19.default.bold(localProject.slug)} ${import_picocolors19.default.dim(`(${localProject.id})`)} ${import_picocolors19.default.yellow("(unconfirmed)")}`;
284363
284765
  } else {
284364
- projectLine = `${import_picocolors18.default.bold("default")} ${target ? import_picocolors18.default.yellow("(unconfirmed)") : import_picocolors18.default.dim("(org-default)")}`;
284766
+ projectLine = `${import_picocolors19.default.bold("default")} ${target ? import_picocolors19.default.yellow("(unconfirmed)") : import_picocolors19.default.dim("(org-default)")}`;
284365
284767
  }
284366
- console.log(` ${import_picocolors18.default.dim("project")} ${projectLine}`);
284768
+ console.log(` ${import_picocolors19.default.dim("project")} ${projectLine}`);
284367
284769
  const branch = confirmed?.branchId ?? null;
284368
284770
  const env = confirmed?.environment ?? target?.keyEnv ?? null;
284369
284771
  if (branch) {
284370
284772
  const label = confirmed?.branchRoot ? "production root" : `branch ${branch}`;
284371
- console.log(` ${import_picocolors18.default.dim("acts on")} ${import_picocolors18.default.bold(label)}`);
284773
+ console.log(` ${import_picocolors19.default.dim("acts on")} ${import_picocolors19.default.bold(label)}`);
284372
284774
  } else if (env) {
284373
- const suffix = confirmed ? "" : ` ${import_picocolors18.default.yellow("(unconfirmed)")}`;
284374
- console.log(` ${import_picocolors18.default.dim("acts on")} ${import_picocolors18.default.bold(env)}${suffix}`);
284775
+ const suffix = confirmed ? "" : ` ${import_picocolors19.default.yellow("(unconfirmed)")}`;
284776
+ console.log(` ${import_picocolors19.default.dim("acts on")} ${import_picocolors19.default.bold(env)}${suffix}`);
284375
284777
  }
284376
284778
  const divergence = describeMismatches(target?.mismatches ?? []);
284377
- if (divergence) console.log(` ${import_picocolors18.default.yellow(`\u26A0 ${divergence}`)}`);
284779
+ if (divergence) console.log(` ${import_picocolors19.default.yellow(`\u26A0 ${divergence}`)}`);
284378
284780
  }
284379
284781
  async function status(args = []) {
284380
284782
  const apiUrl3 = apiBaseUrl();
284381
284783
  const cfg = readConfig();
284382
284784
  const mode = getMode();
284383
- const effective = resolveEffectiveApiKey();
284384
- const target = effective.key ? await resolveTarget({ url: apiUrl3, apiKey: effective.key, keySource: effective.source ?? "stored" }) : null;
284785
+ const runtimeKey = resolveRuntimeApiKey();
284786
+ const target = runtimeKey.key ? await resolveTarget({ url: apiUrl3, apiKey: runtimeKey.key, keySource: runtimeKey.source ?? "stored" }) : null;
284385
284787
  if (args.includes("--json")) {
284386
284788
  const entry = getKeyEntry(mode);
284387
- const key2 = describeEffectiveKey(mode, process.env.ABLO_API_KEY, entry);
284388
- const plan2 = resolvePushPlan();
284389
284789
  const activeProject2 = getActiveProject();
284390
- const pushed2 = await fetchPushedSchema(apiUrl3, effective.key);
284790
+ const pushed2 = await fetchPushedSchema(apiUrl3, runtimeKey.key);
284391
284791
  const reachableForJson = await ping(apiUrl3);
284392
- const dataSource2 = reachableForJson ? (await fetchRoutingState(apiUrl3, effective.key)).source : { kind: "unknown", detail: "unreachable" };
284792
+ const dataSource2 = reachableForJson ? (await fetchRoutingState(apiUrl3, runtimeKey.key)).source : { kind: "unknown", detail: "unreachable" };
284393
284793
  const driftForJson = schemaDrift(await readLocalSchemaHash(), pushed2?.hash);
284394
284794
  const out = {
284395
284795
  // The locally-active project (`ablo projects use`); null = org-default.
@@ -284397,28 +284797,20 @@ async function status(args = []) {
284397
284797
  // The credential the CLI resolves for requests, with its source —
284398
284798
  // 'env' | '.env.local' | '.env' | 'stored'. Key confusion is a common
284399
284799
  // source of trouble, and the source is usually the answer.
284400
- effectiveKey: {
284401
- prefix: effective.key ? effective.key.slice(0, 12) : null,
284402
- source: effective.source,
284800
+ runtimeKey: {
284801
+ prefix: runtimeKey.key ? runtimeKey.key.slice(0, 12) : null,
284802
+ source: runtimeKey.source,
284403
284803
  // What this credential can do. A pipeline that pushes can read it before
284404
284804
  // running the push, rather than learning from the 403 — the same fact
284405
284805
  // the human output prints, from the same place.
284406
- kind: credentialCapability(effective.key).kind
284806
+ kind: credentialCapability(runtimeKey.key).kind
284407
284807
  },
284408
- keyPrefix: key2.keyPrefix,
284409
- keySource: key2.keySource,
284410
- keyMode: key2.keyMode,
284411
- storedKeyPrefix: key2.storedKeyPrefix,
284412
- keyMatchesActiveMode: key2.keyMatchesActiveMode,
284413
- keyMatchesStoredActiveKey: key2.keyMatchesStoredActiveKey,
284414
- keyMismatch: key2.keyMismatch,
284415
284808
  organizationId: entry?.organizationId ?? null,
284416
284809
  // The SERVER-CONFIRMED plane this key resolves to — the authoritative
284417
284810
  // answer to "where does a push land", independent of the local
284418
284811
  // project preference above. Null when the server didn't answer.
284419
284812
  confirmedTarget: target?.confirmed ? {
284420
284813
  organizationId: target.confirmed.organizationId,
284421
- environment: target.confirmed.environment,
284422
284814
  project: target.confirmed.project,
284423
284815
  projectId: target.confirmed.projectId,
284424
284816
  branchId: target.confirmed.branchId,
@@ -284426,14 +284818,7 @@ async function status(args = []) {
284426
284818
  } : null,
284427
284819
  // Divergence between local project intent and the confirmed target.
284428
284820
  mismatches: target?.mismatches ?? [],
284429
- // What `ablo push` would do right now — the answer to "why did push
284430
- // demand a different key".
284431
- push: {
284432
- flow: plan2.flow,
284433
- keyPrefix: plan2.apiKey?.slice(0, 12) ?? null,
284434
- keySource: plan2.source
284435
- },
284436
- // The schema active for this key's environment — the typename and conflict
284821
+ // The schema active for this key's branch — the typename and conflict
284437
284822
  // rules the engine enforces, which may differ from your local `schema.ts`.
284438
284823
  // null means the server did not answer (unreachable, too old, or no key).
284439
284824
  schema: pushed2 ? {
@@ -284453,7 +284838,7 @@ async function status(args = []) {
284453
284838
  drift: driftForJson,
284454
284839
  blockers: blockers({
284455
284840
  reachable: reachableForJson,
284456
- hasKey: Boolean(effective.key),
284841
+ hasKey: Boolean(runtimeKey.key),
284457
284842
  dataSource: dataSource2,
284458
284843
  schemaPushed: Boolean(pushed2?.active),
284459
284844
  drift: driftForJson
@@ -284463,26 +284848,31 @@ async function status(args = []) {
284463
284848
  return;
284464
284849
  }
284465
284850
  console.log(`
284466
- ${brand("ablo")} ${import_picocolors18.default.dim("status")}
284851
+ ${brand("ablo")} ${import_picocolors19.default.dim("status")}
284467
284852
  `);
284468
- if (effective.key && effective.source && effective.source !== "stored") {
284469
- const label = effective.source === "env" ? "ABLO_API_KEY env" : effective.source;
284853
+ if (runtimeKey.key && runtimeKey.source && runtimeKey.source !== "stored") {
284854
+ const label = runtimeKey.source === "env" ? "ABLO_API_KEY env" : runtimeKey.source;
284470
284855
  console.log(
284471
- ` ${import_picocolors18.default.dim("key")} ${effective.key.slice(0, 12)}\u2026 ${import_picocolors18.default.dim(`(${label} \u2014 overrides stored)`)}`
284856
+ ` ${import_picocolors19.default.dim("key")} ${runtimeKey.key.slice(0, 12)}\u2026 ${import_picocolors19.default.dim(`(${label} \u2014 overrides stored)`)}`
284472
284857
  );
284473
284858
  } else if (!cfg) {
284474
- console.log(` ${import_picocolors18.default.yellow("!")} Not logged in \u2014 run ${import_picocolors18.default.bold("ablo login")}.`);
284859
+ console.log(` ${import_picocolors19.default.yellow("!")} Not logged in \u2014 run ${import_picocolors19.default.bold("ablo login")}.`);
284475
284860
  }
284476
284861
  const activeEntry = getKeyEntry(mode);
284477
- const key = describeEffectiveKey(mode, process.env.ABLO_API_KEY, activeEntry);
284478
- if (key.keyMismatch) {
284479
- console.log(` ${import_picocolors18.default.yellow(`! ${key.keyMismatch.message}`)}`);
284480
- }
284481
284862
  const activeProject = getActiveProject();
284482
- printTargetLines(target, activeProject, activeEntry?.organizationId);
284863
+ printTargetLines(
284864
+ target,
284865
+ activeProject,
284866
+ activeEntry?.organizationId,
284867
+ activeEntry?.organizationSlug
284868
+ );
284869
+ const management = getManagementKeyEntry();
284870
+ console.log(
284871
+ ` ${import_picocolors19.default.dim("\u25CB")} ${"management".padEnd(12)} ${management ? import_picocolors19.default.dim(`${management.apiKey.slice(0, 12)}\u2026${management.expiresAt ? ` \xB7 ${expiryLabel(management.expiresAt)}` : ""}`) : import_picocolors19.default.dim("\u2014 no key")}`
284872
+ );
284483
284873
  for (const { key: m2, label } of [
284484
- { key: "sandbox", label: "management" },
284485
- { key: "production", label: "observer" }
284874
+ { key: "sandbox", label: "legacy child" },
284875
+ { key: "production", label: "legacy root" }
284486
284876
  ]) {
284487
284877
  const entry = getKeyEntry(m2);
284488
284878
  if (entry) {
@@ -284490,106 +284880,104 @@ async function status(args = []) {
284490
284880
  credentialCapability(entry.apiKey).label,
284491
284881
  entry.expiresAt ? expiryLabel(entry.expiresAt) : ""
284492
284882
  ].filter(Boolean);
284493
- const trail = facts.length ? ` ${import_picocolors18.default.dim("\xB7")} ${facts.join(import_picocolors18.default.dim(" \xB7 "))}` : "";
284883
+ const trail = facts.length ? ` ${import_picocolors19.default.dim("\xB7")} ${facts.join(import_picocolors19.default.dim(" \xB7 "))}` : "";
284494
284884
  console.log(
284495
- ` ${import_picocolors18.default.dim("\u25CB")} ${label.padEnd(10)} ${import_picocolors18.default.dim(`${entry.apiKey.slice(0, 12)}\u2026`)}${trail}`
284885
+ ` ${import_picocolors19.default.dim("\u25CB")} ${label.padEnd(12)} ${import_picocolors19.default.dim(`${entry.apiKey.slice(0, 12)}\u2026`)}${trail}`
284496
284886
  );
284497
- } else {
284498
- console.log(` ${import_picocolors18.default.dim("\u25CB")} ${label.padEnd(10)} ${import_picocolors18.default.dim("\u2014 no key")}`);
284499
284887
  }
284500
284888
  }
284501
- const plan = resolvePushPlan();
284889
+ const pushBranch = target?.confirmed?.branchRoot === true ? "production root" : target?.confirmed?.branchId ? `branch ${target.confirmed.branchId}` : runtimeKey.key ? "unknown branch" : "no branch";
284502
284890
  console.log(
284503
- ` ${import_picocolors18.default.dim("push")} ${plan.apiKey ? `${import_picocolors18.default.bold(plan.flow)} ${import_picocolors18.default.dim(`with ${plan.apiKey.slice(0, 12)}\u2026 (${plan.source})`)}` : `${import_picocolors18.default.bold(plan.flow)} ${import_picocolors18.default.yellow("\u2014 no credential")} ${import_picocolors18.default.dim(`(run ${import_picocolors18.default.bold("ablo login")} or set ${import_picocolors18.default.bold("ABLO_API_KEY")})`)}`}`
284891
+ ` ${import_picocolors19.default.dim("push")} ${runtimeKey.key ? `${import_picocolors19.default.bold(pushBranch)} ${import_picocolors19.default.dim(`with ${runtimeKey.key.slice(0, 12)}\u2026 (${runtimeKey.source})`)}` : `${import_picocolors19.default.bold(pushBranch)} ${import_picocolors19.default.yellow("\u2014 no runtime key")} ${import_picocolors19.default.dim(`(set ${import_picocolors19.default.bold("ABLO_API_KEY")} or run ${import_picocolors19.default.bold("ablo dev")})`)}`}`
284504
284892
  );
284505
- const capability = credentialCapability(effective.key);
284506
- if (capability.note) console.log(` ${import_picocolors18.default.dim(capability.note)}`);
284507
- process.stdout.write(` ${import_picocolors18.default.dim("api")} ${apiUrl3} `);
284893
+ const capability = credentialCapability(runtimeKey.key);
284894
+ if (capability.note) console.log(` ${import_picocolors19.default.dim(capability.note)}`);
284895
+ process.stdout.write(` ${import_picocolors19.default.dim("api")} ${apiUrl3} `);
284508
284896
  const reachable = await ping(apiUrl3);
284509
- console.log(reachable ? import_picocolors18.default.green("reachable") : import_picocolors18.default.red("unreachable"));
284510
- const introspectKey = effective.key;
284897
+ console.log(reachable ? import_picocolors19.default.green("reachable") : import_picocolors19.default.red("unreachable"));
284898
+ const introspectKey = runtimeKey.key;
284511
284899
  const { source: dataSource, validation } = reachable ? await fetchRoutingState(apiUrl3, introspectKey) : { source: { kind: "unknown", detail: "unreachable" }, validation: null };
284512
284900
  if (dataSource.kind === "connected") {
284513
284901
  const how = [...new Set(dataSource.connections)].join(" + ");
284514
284902
  const pooled = detectPoolerIn(dataSource.hosts);
284515
284903
  const unreachable = validation && !validation.ok ? validation.message : void 0;
284516
- console.log(` ${import_picocolors18.default.dim("data")} ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.dim(`database connected to this plane (${how})`)}`);
284904
+ console.log(` ${import_picocolors19.default.dim("data")} ${import_picocolors19.default.green("\u2713")} ${import_picocolors19.default.dim(`database connected to this plane (${how})`)}`);
284517
284905
  if (pooled) {
284518
284906
  console.log(
284519
- ` ${import_picocolors18.default.yellow("\u26A0")} ${import_picocolors18.default.dim(
284907
+ ` ${import_picocolors19.default.yellow("\u26A0")} ${import_picocolors19.default.dim(
284520
284908
  `${pooled.host} is a connection pooler` + (pooled.direct ? `; register the direct host instead: ${pooled.direct}` : "; register the direct host instead")
284521
284909
  )}`
284522
284910
  );
284523
284911
  }
284524
284912
  if (unreachable) {
284525
- console.log(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.dim(`Ablo could not reach it \u2014 ${unreachable}`)}`);
284913
+ console.log(` ${import_picocolors19.default.red("\u2717")} ${import_picocolors19.default.dim(`Ablo could not reach it \u2014 ${unreachable}`)}`);
284526
284914
  }
284527
284915
  } else if (dataSource.kind === "none") {
284528
284916
  console.log(
284529
- ` ${import_picocolors18.default.dim("data")} ${import_picocolors18.default.red("\u2717 no database connected to this plane")} ${import_picocolors18.default.dim("\u2014 writes are held")}`
284917
+ ` ${import_picocolors19.default.dim("data")} ${import_picocolors19.default.red("\u2717 no database connected to this plane")} ${import_picocolors19.default.dim("\u2014 writes are held")}`
284530
284918
  );
284531
284919
  } else if (reachable) {
284532
- console.log(` ${import_picocolors18.default.dim("data")} ${import_picocolors18.default.yellow("?")} ${import_picocolors18.default.dim(`could not read the plane's databases (${dataSource.detail})`)}`);
284920
+ console.log(` ${import_picocolors19.default.dim("data")} ${import_picocolors19.default.yellow("?")} ${import_picocolors19.default.dim(`could not read the plane's databases (${dataSource.detail})`)}`);
284533
284921
  }
284534
284922
  const pushed = reachable ? await fetchPushedSchema(apiUrl3, introspectKey) : null;
284535
284923
  if (reachable) {
284536
284924
  if (pushed?.active) {
284537
- const when = pushed.pushedAt ? ` ${import_picocolors18.default.dim(`@ ${pushed.pushedAt.slice(0, 10)}`)}` : "";
284538
- const ver = pushed.version != null ? ` ${import_picocolors18.default.dim(`(rev ${pushed.version})`)}` : "";
284539
- const hashLabel = pushed.hash ? ` ${import_picocolors18.default.dim(`hash ${pushed.hash}`)}` : "";
284540
- console.log(` ${import_picocolors18.default.dim("schema")} ${import_picocolors18.default.bold(`${pushed.models.length} models pushed`)}${ver}${hashLabel}${when}`);
284925
+ const when = pushed.pushedAt ? ` ${import_picocolors19.default.dim(`@ ${pushed.pushedAt.slice(0, 10)}`)}` : "";
284926
+ const ver = pushed.version != null ? ` ${import_picocolors19.default.dim(`(rev ${pushed.version})`)}` : "";
284927
+ const hashLabel = pushed.hash ? ` ${import_picocolors19.default.dim(`hash ${pushed.hash}`)}` : "";
284928
+ console.log(` ${import_picocolors19.default.dim("schema")} ${import_picocolors19.default.bold(`${pushed.models.length} models pushed`)}${ver}${hashLabel}${when}`);
284541
284929
  for (const m2 of pushed.models) {
284542
- const tn = m2.typename === m2.key ? import_picocolors18.default.dim(`typename=${m2.typename}`) : import_picocolors18.default.yellow(`typename=${m2.typename}`);
284930
+ const tn = m2.typename === m2.key ? import_picocolors19.default.dim(`typename=${m2.typename}`) : import_picocolors19.default.yellow(`typename=${m2.typename}`);
284543
284931
  const conflict = formatConflict(m2.conflict);
284544
- const conflictStr2 = conflict ? ` ${import_picocolors18.default.dim(`conflict=${conflict}`)}` : "";
284545
- console.log(` ${import_picocolors18.default.dim("\u2022")} ${m2.key.padEnd(14)} ${tn}${conflictStr2}`);
284932
+ const conflictStr2 = conflict ? ` ${import_picocolors19.default.dim(`conflict=${conflict}`)}` : "";
284933
+ console.log(` ${import_picocolors19.default.dim("\u2022")} ${m2.key.padEnd(14)} ${tn}${conflictStr2}`);
284546
284934
  }
284547
284935
  } else if (pushed && !pushed.active) {
284548
- console.log(` ${import_picocolors18.default.dim("schema")} ${import_picocolors18.default.yellow("none pushed")} ${import_picocolors18.default.dim(`(run ${import_picocolors18.default.bold("ablo push")} or ${import_picocolors18.default.bold("ablo dev")})`)}`);
284936
+ console.log(` ${import_picocolors19.default.dim("schema")} ${import_picocolors19.default.yellow("none pushed")} ${import_picocolors19.default.dim(`(run ${import_picocolors19.default.bold("ablo push")} or ${import_picocolors19.default.bold("ablo dev")})`)}`);
284549
284937
  }
284550
284938
  }
284551
284939
  const drift = schemaDrift(await readLocalSchemaHash(), pushed?.hash);
284552
284940
  if (drift) {
284553
284941
  console.log(
284554
- ` ${import_picocolors18.default.dim("drift")} ${import_picocolors18.default.red("\u2717 local schema differs from the server")} ` + import_picocolors18.default.dim(`(local ${drift.local}, server ${drift.server})`)
284942
+ ` ${import_picocolors19.default.dim("drift")} ${import_picocolors19.default.red("\u2717 local schema differs from the server")} ` + import_picocolors19.default.dim(`(local ${drift.local}, server ${drift.server})`)
284555
284943
  );
284556
284944
  }
284557
284945
  const found = blockers({
284558
284946
  reachable,
284559
- hasKey: Boolean(effective.key),
284947
+ hasKey: Boolean(runtimeKey.key),
284560
284948
  dataSource,
284561
284949
  schemaPushed: Boolean(pushed?.active),
284562
284950
  drift
284563
284951
  });
284564
284952
  console.log();
284565
284953
  if (found.length > 0) {
284566
- console.log(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.bold("writes would fail right now")}`);
284954
+ console.log(` ${import_picocolors19.default.red("\u2717")} ${import_picocolors19.default.bold("writes would fail right now")}`);
284567
284955
  for (const b4 of found) {
284568
- console.log(` ${import_picocolors18.default.dim("\xB7")} ${b4.problem}`);
284569
- console.log(` ${import_picocolors18.default.dim(b4.fix)}`);
284956
+ console.log(` ${import_picocolors19.default.dim("\xB7")} ${b4.problem}`);
284957
+ console.log(` ${import_picocolors19.default.dim(b4.fix)}`);
284570
284958
  }
284571
284959
  } else if (dataSource.kind === "unknown") {
284572
284960
  console.log(
284573
- ` ${import_picocolors18.default.yellow("?")} ${import_picocolors18.default.dim("nothing is blocking a write, but this key could not read the plane's databases \u2014 some checks were skipped")}`
284961
+ ` ${import_picocolors19.default.yellow("?")} ${import_picocolors19.default.dim("nothing is blocking a write, but this key could not read the plane's databases \u2014 some checks were skipped")}`
284574
284962
  );
284575
284963
  } else {
284576
- console.log(` ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.dim("ready \u2014 a write should succeed")}`);
284964
+ console.log(` ${import_picocolors19.default.green("\u2713")} ${import_picocolors19.default.dim("ready \u2014 a write should succeed")}`);
284577
284965
  }
284578
284966
  console.log();
284579
284967
  }
284580
284968
 
284581
284969
  // src/doctor.ts
284582
284970
  init_cjs_shims();
284583
- var import_picocolors19 = __toESM(require_picocolors(), 1);
284971
+ var import_picocolors20 = __toESM(require_picocolors(), 1);
284584
284972
  init_theme();
284585
284973
  init_controlPlane();
284586
284974
  init_config();
284587
284975
  init_target();
284588
284976
  init_readiness();
284589
284977
  function render(check2) {
284590
- const mark = check2.state === "ok" ? import_picocolors19.default.green("\u2713") : check2.state === "fail" ? import_picocolors19.default.red("\u2717") : import_picocolors19.default.dim("\u2013");
284591
- console.log(` ${mark} ${check2.label.padEnd(10)} ${check2.state === "fail" ? check2.detail : import_picocolors19.default.dim(check2.detail)}`);
284592
- if (check2.fix) console.log(` ${" ".repeat(11)}${import_picocolors19.default.dim(`\u2192 ${check2.fix}`)}`);
284978
+ const mark = check2.state === "ok" ? import_picocolors20.default.green("\u2713") : check2.state === "fail" ? import_picocolors20.default.red("\u2717") : import_picocolors20.default.dim("\u2013");
284979
+ console.log(` ${mark} ${check2.label.padEnd(10)} ${check2.state === "fail" ? check2.detail : import_picocolors20.default.dim(check2.detail)}`);
284980
+ if (check2.fix) console.log(` ${" ".repeat(11)}${import_picocolors20.default.dim(`\u2192 ${check2.fix}`)}`);
284593
284981
  }
284594
284982
  async function ping2(apiUrl3) {
284595
284983
  const ctrl = new AbortController();
@@ -284607,16 +284995,16 @@ async function ping2(apiUrl3) {
284607
284995
  }
284608
284996
  async function doctor() {
284609
284997
  console.log(`
284610
- ${brand("ablo")} ${import_picocolors19.default.dim("doctor")}
284998
+ ${brand("ablo")} ${import_picocolors20.default.dim("doctor")}
284611
284999
  `);
284612
285000
  const apiUrl3 = apiBaseUrl();
284613
- const effective = resolveEffectiveApiKey();
285001
+ const runtimeKey = resolveRuntimeApiKey();
284614
285002
  const checks = [];
284615
- if (effective.key) {
285003
+ if (runtimeKey.key) {
284616
285004
  checks.push({
284617
285005
  label: "key",
284618
285006
  state: "ok",
284619
- detail: `${effective.key.slice(0, 12)}\u2026 from ${effective.source}`
285007
+ detail: `${runtimeKey.key.slice(0, 12)}\u2026 from ${runtimeKey.source}`
284620
285008
  });
284621
285009
  } else {
284622
285010
  checks.push({
@@ -284635,7 +285023,7 @@ async function doctor() {
284635
285023
  fix: "check your connection, then re-run `ablo doctor`"
284636
285024
  }
284637
285025
  );
284638
- const target = effective.key ? await resolveTarget({ url: apiUrl3, apiKey: effective.key, keySource: effective.source ?? "stored" }) : null;
285026
+ const target = runtimeKey.key ? await resolveTarget({ url: apiUrl3, apiKey: runtimeKey.key, keySource: runtimeKey.source ?? "stored" }) : null;
284639
285027
  const confirmed = target?.confirmed ?? null;
284640
285028
  if (confirmed) {
284641
285029
  const project = confirmed.project ? confirmed.project.isDefault ? "default" : confirmed.project.name === null ? `${confirmed.project.id} (unnamed \u2014 ${confirmed.project.unnamedReason ?? "the project list did not answer"})` : `${confirmed.project.slug} (${confirmed.project.id})` : "default";
@@ -284648,12 +285036,12 @@ async function doctor() {
284648
285036
  const local = getActiveProject();
284649
285037
  checks.push({
284650
285038
  label: "identity",
284651
- state: effective.key && reachable ? "fail" : "skip",
284652
- detail: effective.key ? `the server did not resolve this key${local ? ` (locally selected: ${local.slug})` : ""}` : "no key to resolve",
284653
- fix: effective.key ? "check the key is valid and not expired \u2014 `ablo login` mints a fresh pair" : void 0
285039
+ state: runtimeKey.key && reachable ? "fail" : "skip",
285040
+ detail: runtimeKey.key ? `the server did not resolve this key${local ? ` (locally selected: ${local.slug})` : ""}` : "no key to resolve",
285041
+ fix: runtimeKey.key ? "check the key is valid and not expired \u2014 `ablo login` mints a fresh pair" : void 0
284654
285042
  });
284655
285043
  }
284656
- const { source: dataSource, validation } = reachable ? await fetchRoutingState(apiUrl3, effective.key) : { source: { kind: "unknown", detail: "unreachable" }, validation: null };
285044
+ const { source: dataSource, validation } = reachable ? await fetchRoutingState(apiUrl3, runtimeKey.key) : { source: { kind: "unknown", detail: "unreachable" }, validation: null };
284657
285045
  if (dataSource.kind === "connected") {
284658
285046
  const how = [...new Set(dataSource.connections)].join(" + ");
284659
285047
  const pooled = detectPoolerIn(dataSource.hosts);
@@ -284675,13 +285063,13 @@ async function doctor() {
284675
285063
  } else {
284676
285064
  checks.push({ label: "data", state: "skip", detail: `not determined (${dataSource.detail})` });
284677
285065
  }
284678
- const pushed = reachable ? await fetchPushedSchema(apiUrl3, effective.key) : null;
285066
+ const pushed = reachable ? await fetchPushedSchema(apiUrl3, runtimeKey.key) : null;
284679
285067
  checks.push(
284680
285068
  pushed?.active ? {
284681
285069
  label: "schema",
284682
285070
  state: "ok",
284683
285071
  detail: `${pushed.models.length} models active${pushed.hash ? `, hash ${pushed.hash}` : ""}`
284684
- } : reachable && effective.key ? { label: "schema", state: "fail", detail: "none active for this key", fix: "run `ablo push`" } : { label: "schema", state: "skip", detail: "not determined" }
285072
+ } : reachable && runtimeKey.key ? { label: "schema", state: "fail", detail: "none active for this key", fix: "run `ablo push`" } : { label: "schema", state: "skip", detail: "not determined" }
284685
285073
  );
284686
285074
  const drift = schemaDrift(await readLocalSchemaHash(), pushed?.hash);
284687
285075
  if (drift) {
@@ -284712,7 +285100,7 @@ async function doctor() {
284712
285100
  for (const check2 of checks) render(check2);
284713
285101
  const blocking = blockers({
284714
285102
  reachable,
284715
- hasKey: Boolean(effective.key),
285103
+ hasKey: Boolean(runtimeKey.key),
284716
285104
  dataSource,
284717
285105
  schemaPushed: Boolean(pushed?.active),
284718
285106
  drift
@@ -284722,15 +285110,15 @@ async function doctor() {
284722
285110
  console.log();
284723
285111
  if (failed > 0) {
284724
285112
  console.log(
284725
- ` ${import_picocolors19.default.red("\u2717")} ${import_picocolors19.default.bold(`${failed} problem${failed === 1 ? "" : "s"}`)}` + import_picocolors19.default.dim(skipped > 0 ? `, ${skipped} check${skipped === 1 ? "" : "s"} skipped` : "")
285113
+ ` ${import_picocolors20.default.red("\u2717")} ${import_picocolors20.default.bold(`${failed} problem${failed === 1 ? "" : "s"}`)}` + import_picocolors20.default.dim(skipped > 0 ? `, ${skipped} check${skipped === 1 ? "" : "s"} skipped` : "")
284726
285114
  );
284727
- console.log(import_picocolors19.default.dim(" Fix them in the order above \u2014 an earlier one often explains a later one."));
285115
+ console.log(import_picocolors20.default.dim(" Fix them in the order above \u2014 an earlier one often explains a later one."));
284728
285116
  } else if (skipped > 0) {
284729
285117
  console.log(
284730
- ` ${import_picocolors19.default.yellow("?")} ${import_picocolors19.default.dim(`nothing is blocking a write, but ${skipped} check${skipped === 1 ? "" : "s"} could not be run`)}`
285118
+ ` ${import_picocolors20.default.yellow("?")} ${import_picocolors20.default.dim(`nothing is blocking a write, but ${skipped} check${skipped === 1 ? "" : "s"} could not be run`)}`
284731
285119
  );
284732
285120
  } else {
284733
- console.log(` ${import_picocolors19.default.green("\u2713")} ${import_picocolors19.default.dim("everything checks out \u2014 a write should succeed")}`);
285121
+ console.log(` ${import_picocolors20.default.green("\u2713")} ${import_picocolors20.default.dim("everything checks out \u2014 a write should succeed")}`);
284734
285122
  }
284735
285123
  console.log();
284736
285124
  if (blocking.length > 0 || failed > 0) process.exitCode = 1;
@@ -284738,9 +285126,9 @@ async function doctor() {
284738
285126
 
284739
285127
  // src/logs.ts
284740
285128
  init_cjs_shims();
284741
- var import_errors15 = require("@abloatai/transaction/errors");
284742
- var import_wire8 = require("@abloatai/transaction/wire");
284743
- var import_picocolors20 = __toESM(require_picocolors(), 1);
285129
+ var import_errors16 = require("@abloatai/transaction/errors");
285130
+ var import_wire9 = require("@abloatai/transaction/wire");
285131
+ var import_picocolors21 = __toESM(require_picocolors(), 1);
284744
285132
  init_config();
284745
285133
  init_theme();
284746
285134
  init_controlPlane();
@@ -284751,8 +285139,7 @@ function parseLogsArgs(argv) {
284751
285139
  since: void 0,
284752
285140
  model: void 0,
284753
285141
  op: void 0,
284754
- json: false,
284755
- mode: void 0
285142
+ json: false
284756
285143
  };
284757
285144
  for (let i = 0; i < argv.length; i++) {
284758
285145
  const arg = argv[i];
@@ -284780,15 +285167,13 @@ function parseLogsArgs(argv) {
284780
285167
  case "--json":
284781
285168
  args.json = true;
284782
285169
  break;
284783
- case "--mode": {
284784
- const raw = argv[++i];
284785
- const m2 = normalizeMode(raw);
284786
- if (!m2) throw new import_errors15.AbloValidationError(`--mode expects "sandbox" or "production", got "${raw}"`, { code: "cli_invalid_arguments" });
284787
- args.mode = m2;
284788
- break;
284789
- }
285170
+ case "--mode":
285171
+ throw new import_errors16.AbloValidationError(
285172
+ "--mode was removed. Logs follow the branch bound to ABLO_API_KEY; select a different branch by supplying its key.",
285173
+ { code: "cli_invalid_arguments" }
285174
+ );
284790
285175
  default:
284791
- throw new import_errors15.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285176
+ throw new import_errors16.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
284792
285177
  }
284793
285178
  }
284794
285179
  return args;
@@ -284807,10 +285192,10 @@ function resolveSince(since) {
284807
285192
  var sleep2 = (ms) => new Promise((r2) => setTimeout(r2, ms));
284808
285193
  function colorOp(op) {
284809
285194
  const label = op.padEnd(6);
284810
- if (op === "create") return import_picocolors20.default.green(label);
284811
- if (op === "update") return import_picocolors20.default.yellow(label);
284812
- if (op === "delete") return import_picocolors20.default.red(label);
284813
- return import_picocolors20.default.dim(label);
285195
+ if (op === "create") return import_picocolors21.default.green(label);
285196
+ if (op === "update") return import_picocolors21.default.yellow(label);
285197
+ if (op === "delete") return import_picocolors21.default.red(label);
285198
+ return import_picocolors21.default.dim(label);
284814
285199
  }
284815
285200
  function render2(e2, json) {
284816
285201
  if (json) {
@@ -284819,21 +285204,21 @@ function render2(e2, json) {
284819
285204
  return;
284820
285205
  }
284821
285206
  const t = new Date(e2.at).toLocaleTimeString();
284822
- const actor = e2.actor ? import_picocolors20.default.dim(` ${e2.actor}`) : "";
284823
- console.log(` ${import_picocolors20.default.dim(t)} ${colorOp(e2.op)} ${import_picocolors20.default.bold(e2.model)} ${import_picocolors20.default.dim(e2.recordId)}${actor}`);
285207
+ const actor = e2.actor ? import_picocolors21.default.dim(` ${e2.actor}`) : "";
285208
+ console.log(` ${import_picocolors21.default.dim(t)} ${colorOp(e2.op)} ${import_picocolors21.default.bold(e2.model)} ${import_picocolors21.default.dim(e2.recordId)}${actor}`);
284824
285209
  }
284825
285210
  async function logs(argv) {
284826
285211
  let args;
284827
285212
  try {
284828
285213
  args = parseLogsArgs(argv);
284829
285214
  } catch (err) {
284830
- console.error(import_picocolors20.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285215
+ console.error(import_picocolors21.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284831
285216
  process.exit(1);
284832
285217
  }
284833
- const apiKey = resolveApiKey(args.mode);
285218
+ const apiKey = resolveRuntimeApiKey().key;
284834
285219
  if (!apiKey) {
284835
285220
  console.error(
284836
- import_picocolors20.default.red(` No API key.`) + import_picocolors20.default.dim(` Run ${import_picocolors20.default.bold("ablo login")} or set ${import_picocolors20.default.bold("ABLO_API_KEY")}.`)
285221
+ import_picocolors21.default.red(` No API key.`) + import_picocolors21.default.dim(` Run ${import_picocolors21.default.bold("ablo login")} or set ${import_picocolors21.default.bold("ABLO_API_KEY")}.`)
284837
285222
  );
284838
285223
  process.exit(1);
284839
285224
  }
@@ -284847,18 +285232,18 @@ async function logs(argv) {
284847
285232
  if (!res) return null;
284848
285233
  if (!res.ok) {
284849
285234
  const body = await res.json().catch(() => ({}));
284850
- console.error(import_picocolors20.default.red(` logs failed (${res.status}): ${body.reason ?? body.message ?? ""}`));
285235
+ console.error(import_picocolors21.default.red(` logs failed (${res.status}): ${body.reason ?? body.message ?? ""}`));
284851
285236
  process.exit(1);
284852
285237
  }
284853
285238
  const json = await res.json();
284854
285239
  return {
284855
285240
  events: json.data ?? json.events ?? [],
284856
- cursor: json.next_cursor ?? (json.cursor != null ? (0, import_wire8.formatFeedCursor)({ log: json.cursor, claims: 0 }) : (0, import_wire8.formatFeedCursor)(import_wire8.FEED_CURSOR_START))
285241
+ cursor: json.next_cursor ?? (json.cursor != null ? (0, import_wire9.formatFeedCursor)({ log: json.cursor, claims: 0 }) : (0, import_wire9.formatFeedCursor)(import_wire9.FEED_CURSOR_START))
284857
285242
  };
284858
285243
  }
284859
285244
  if (!args.json) {
284860
285245
  console.log(`
284861
- ${brand("ablo")} ${import_picocolors20.default.dim("logs")} ${import_picocolors20.default.dim(`(${args.mode ?? "active"} mode)`)}
285246
+ ${brand("ablo")} ${import_picocolors21.default.dim("logs")}
284862
285247
  `);
284863
285248
  }
284864
285249
  const initial = await fetchPage({
@@ -284868,13 +285253,13 @@ async function logs(argv) {
284868
285253
  ...args.op ? { op: args.op } : {}
284869
285254
  });
284870
285255
  if (!initial) {
284871
- console.error(import_picocolors20.default.red(` Couldn't reach ${baseUrl2}.`));
285256
+ console.error(import_picocolors21.default.red(` Couldn't reach ${baseUrl2}.`));
284872
285257
  process.exit(1);
284873
285258
  }
284874
285259
  for (const e2 of initial.events) render2(e2, args.json);
284875
285260
  let cursor = initial.cursor;
284876
285261
  if (!args.follow) return;
284877
- if (!args.json) console.log(` ${import_picocolors20.default.dim("watching for new activity \u2026 (Ctrl-C to stop)")}
285262
+ if (!args.json) console.log(` ${import_picocolors21.default.dim("watching for new activity \u2026 (Ctrl-C to stop)")}
284878
285263
  `);
284879
285264
  for (; ; ) {
284880
285265
  await sleep2(1500);
@@ -284885,16 +285270,16 @@ async function logs(argv) {
284885
285270
  });
284886
285271
  if (!page) continue;
284887
285272
  for (const e2 of page.events) render2(e2, args.json);
284888
- const prev = (0, import_wire8.parseFeedCursor)(cursor);
284889
- const next = (0, import_wire8.parseFeedCursor)(page.cursor);
284890
- if (prev && next && (0, import_wire8.feedCursorAdvanced)(prev, next)) cursor = page.cursor;
285273
+ const prev = (0, import_wire9.parseFeedCursor)(cursor);
285274
+ const next = (0, import_wire9.parseFeedCursor)(page.cursor);
285275
+ if (prev && next && (0, import_wire9.feedCursorAdvanced)(prev, next)) cursor = page.cursor;
284891
285276
  }
284892
285277
  }
284893
285278
 
284894
285279
  // src/webhooks.ts
284895
285280
  init_cjs_shims();
284896
285281
  var import_fs9 = require("fs");
284897
- var import_picocolors21 = __toESM(require_picocolors(), 1);
285282
+ var import_picocolors22 = __toESM(require_picocolors(), 1);
284898
285283
  var import_credentialPolicy4 = require("@abloatai/transaction/auth/credentialPolicy");
284899
285284
  init_config();
284900
285285
  init_theme();
@@ -284924,15 +285309,15 @@ function positional(args) {
284924
285309
  return void 0;
284925
285310
  }
284926
285311
  function requireKey3(mode) {
284927
- const apiKey = resolveApiKey(mode);
285312
+ const apiKey = resolveMutationApiKey(mode);
284928
285313
  if (!apiKey) {
284929
285314
  console.error(
284930
- import_picocolors21.default.red(" No API key.") + import_picocolors21.default.dim(` Run ${import_picocolors21.default.bold("ablo login")} or set ${import_picocolors21.default.bold("ABLO_API_KEY")}.`)
285315
+ import_picocolors22.default.red(" No API key.") + import_picocolors22.default.dim(` Run ${import_picocolors22.default.bold("ablo login")} or set ${import_picocolors22.default.bold("ABLO_API_KEY")}.`)
284931
285316
  );
284932
285317
  process.exit(1);
284933
285318
  }
284934
285319
  if ((0, import_credentialPolicy4.classifyCredentialKind)(apiKey) !== "secret") {
284935
- console.error(import_picocolors21.default.red(" Managing webhooks requires a secret key ") + import_picocolors21.default.dim("(sk_test_ / sk_live_)."));
285320
+ console.error(import_picocolors22.default.red(" Managing webhooks requires a branch-bound secret key ") + import_picocolors22.default.dim("(sk_)."));
284936
285321
  process.exit(1);
284937
285322
  }
284938
285323
  return apiKey;
@@ -284948,12 +285333,12 @@ async function api(apiKey, method, path, body) {
284948
285333
  ...body ? { body: JSON.stringify(body) } : {}
284949
285334
  }).catch(() => null);
284950
285335
  if (!res) {
284951
- console.error(import_picocolors21.default.red(` Couldn't reach ${baseUrl()}.`));
285336
+ console.error(import_picocolors22.default.red(` Couldn't reach ${baseUrl()}.`));
284952
285337
  process.exit(1);
284953
285338
  }
284954
285339
  if (!res.ok) {
284955
285340
  const err = await res.json().catch(() => ({}));
284956
- console.error(import_picocolors21.default.red(` Request failed (${res.status}): ${err.message ?? err.reason ?? ""}`));
285341
+ console.error(import_picocolors22.default.red(` Request failed (${res.status}): ${err.message ?? err.reason ?? ""}`));
284957
285342
  process.exit(1);
284958
285343
  }
284959
285344
  return await res.json();
@@ -284975,11 +285360,11 @@ ${line}
284975
285360
  return file;
284976
285361
  }
284977
285362
  function printEndpoint(e2) {
284978
- const dot = e2.status === "enabled" ? import_picocolors21.default.green("\u25CF") : import_picocolors21.default.red("\u25CF");
284979
- const health = e2.last_error ? import_picocolors21.default.red(` last error: ${e2.last_error}`) : "";
284980
- console.log(` ${dot} ${import_picocolors21.default.bold(e2.id)} ${e2.url}`);
285363
+ const dot = e2.status === "enabled" ? import_picocolors22.default.green("\u25CF") : import_picocolors22.default.red("\u25CF");
285364
+ const health = e2.last_error ? import_picocolors22.default.red(` last error: ${e2.last_error}`) : "";
285365
+ console.log(` ${dot} ${import_picocolors22.default.bold(e2.id)} ${e2.url}`);
284981
285366
  console.log(
284982
- import_picocolors21.default.dim(
285367
+ import_picocolors22.default.dim(
284983
285368
  ` ${e2.status} \xB7 ${e2.environment} \xB7 events ${e2.enabled_events.join(",")} \xB7 cursor ${e2.cursor ?? "\u2014"}${health}`
284984
285369
  )
284985
285370
  );
@@ -284991,7 +285376,7 @@ async function webhooks(argv) {
284991
285376
  if (sub === "create") {
284992
285377
  const url = positional(rest);
284993
285378
  if (!url) {
284994
- console.error(import_picocolors21.default.red(" Usage: ") + brand("ablo webhooks create <url>"));
285379
+ console.error(import_picocolors22.default.red(" Usage: ") + brand("ablo webhooks create <url>"));
284995
285380
  process.exit(1);
284996
285381
  }
284997
285382
  const apiKey = requireKey3(mode);
@@ -285003,8 +285388,8 @@ async function webhooks(argv) {
285003
285388
  });
285004
285389
  const file = writeSecretToEnv(created.secret);
285005
285390
  console.log(`
285006
- ${import_picocolors21.default.green("\u2713")} Registered ${import_picocolors21.default.bold(created.id)} \u2192 ${created.url}`);
285007
- console.log(` ${import_picocolors21.default.green("\u2713")} Wrote ${import_picocolors21.default.bold(ENV_KEY)} to ${import_picocolors21.default.bold(file)} ${import_picocolors21.default.dim("(shown once)")}
285391
+ ${import_picocolors22.default.green("\u2713")} Registered ${import_picocolors22.default.bold(created.id)} \u2192 ${created.url}`);
285392
+ console.log(` ${import_picocolors22.default.green("\u2713")} Wrote ${import_picocolors22.default.bold(ENV_KEY)} to ${import_picocolors22.default.bold(file)} ${import_picocolors22.default.dim("(shown once)")}
285008
285393
  `);
285009
285394
  return;
285010
285395
  }
@@ -285012,7 +285397,7 @@ async function webhooks(argv) {
285012
285397
  const apiKey = requireKey3(mode);
285013
285398
  const { data } = await api(apiKey, "GET", "");
285014
285399
  if (data.length === 0) {
285015
- console.log(import_picocolors21.default.dim(" No webhook endpoints. ") + brand("ablo webhooks create <url>"));
285400
+ console.log(import_picocolors22.default.dim(" No webhook endpoints. ") + brand("ablo webhooks create <url>"));
285016
285401
  return;
285017
285402
  }
285018
285403
  console.log();
@@ -285023,40 +285408,40 @@ async function webhooks(argv) {
285023
285408
  if (sub === "roll") {
285024
285409
  const id = positional(rest);
285025
285410
  if (!id) {
285026
- console.error(import_picocolors21.default.red(" Usage: ") + brand("ablo webhooks roll <id>"));
285411
+ console.error(import_picocolors22.default.red(" Usage: ") + brand("ablo webhooks roll <id>"));
285027
285412
  process.exit(1);
285028
285413
  }
285029
285414
  const apiKey = requireKey3(mode);
285030
285415
  const rolled = await api(apiKey, "POST", `/${id}/roll_secret`);
285031
285416
  const file = writeSecretToEnv(rolled.secret);
285032
285417
  console.log(`
285033
- ${import_picocolors21.default.green("\u2713")} Rolled secret for ${import_picocolors21.default.bold(id)} \u2192 ${import_picocolors21.default.bold(file)} ${import_picocolors21.default.dim("(old secret now invalid)")}
285418
+ ${import_picocolors22.default.green("\u2713")} Rolled secret for ${import_picocolors22.default.bold(id)} \u2192 ${import_picocolors22.default.bold(file)} ${import_picocolors22.default.dim("(old secret now invalid)")}
285034
285419
  `);
285035
285420
  return;
285036
285421
  }
285037
285422
  if (sub === "enable") {
285038
285423
  const id = positional(rest);
285039
285424
  if (!id) {
285040
- console.error(import_picocolors21.default.red(" Usage: ") + brand("ablo webhooks enable <id>"));
285425
+ console.error(import_picocolors22.default.red(" Usage: ") + brand("ablo webhooks enable <id>"));
285041
285426
  process.exit(1);
285042
285427
  }
285043
285428
  const apiKey = requireKey3(mode);
285044
285429
  const e2 = await api(apiKey, "POST", `/${id}/enable`);
285045
- console.log(` ${import_picocolors21.default.green("\u2713")} Re-enabled ${import_picocolors21.default.bold(e2.id)}`);
285430
+ console.log(` ${import_picocolors22.default.green("\u2713")} Re-enabled ${import_picocolors22.default.bold(e2.id)}`);
285046
285431
  return;
285047
285432
  }
285048
285433
  if (sub === "rm" || sub === "delete") {
285049
285434
  const id = positional(rest);
285050
285435
  if (!id) {
285051
- console.error(import_picocolors21.default.red(" Usage: ") + brand("ablo webhooks rm <id>"));
285436
+ console.error(import_picocolors22.default.red(" Usage: ") + brand("ablo webhooks rm <id>"));
285052
285437
  process.exit(1);
285053
285438
  }
285054
285439
  const apiKey = requireKey3(mode);
285055
285440
  await api(apiKey, "DELETE", `/${id}`);
285056
- console.log(` ${import_picocolors21.default.green("\u2713")} Removed ${import_picocolors21.default.bold(id)}`);
285441
+ console.log(` ${import_picocolors22.default.green("\u2713")} Removed ${import_picocolors22.default.bold(id)}`);
285057
285442
  return;
285058
285443
  }
285059
- console.log(` ${import_picocolors21.default.bold("Usage:")}`);
285444
+ console.log(` ${import_picocolors22.default.bold("Usage:")}`);
285060
285445
  console.log(` ${brand("ablo webhooks create <url>")} Register an endpoint; writes ${ENV_KEY}`);
285061
285446
  console.log(` ${brand("ablo webhooks list")} List endpoints + delivery health`);
285062
285447
  console.log(` ${brand("ablo webhooks roll <id>")} Mint a fresh signing secret`);
@@ -285067,8 +285452,8 @@ async function webhooks(argv) {
285067
285452
 
285068
285453
  // src/check.ts
285069
285454
  init_cjs_shims();
285070
- var import_errors16 = require("@abloatai/transaction/errors");
285071
- var import_picocolors22 = __toESM(require_picocolors(), 1);
285455
+ var import_errors17 = require("@abloatai/transaction/errors");
285456
+ var import_picocolors23 = __toESM(require_picocolors(), 1);
285072
285457
  init_src();
285073
285458
  var import_schema9 = require("@abloatai/transaction/schema");
285074
285459
  init_push();
@@ -285215,7 +285600,7 @@ function parseCheckArgs(argv) {
285215
285600
  appSchema = argv[++i] ?? appSchema;
285216
285601
  break;
285217
285602
  default:
285218
- throw new import_errors16.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285603
+ throw new import_errors17.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285219
285604
  }
285220
285605
  }
285221
285606
  return { schemaPath, exportName, appSchema };
@@ -285229,22 +285614,22 @@ function hostOf(connectionString) {
285229
285614
  }
285230
285615
  async function reportReadSubject(dbUrl) {
285231
285616
  const host = hostOf(dbUrl);
285232
- console.log(` ${import_picocolors22.default.dim("reading")} ${import_picocolors22.default.bold(host ?? "your database")}`);
285233
- const effective = resolveEffectiveApiKey();
285234
- const state = await fetchDataSourceState(apiBaseUrl(), effective.key);
285617
+ console.log(` ${import_picocolors23.default.dim("reading")} ${import_picocolors23.default.bold(host ?? "your database")}`);
285618
+ const runtimeKey = resolveRuntimeApiKey();
285619
+ const state = await fetchDataSourceState(apiBaseUrl(), runtimeKey.key);
285235
285620
  if (state.kind === "unknown") {
285236
285621
  console.log(
285237
- ` ${import_picocolors22.default.dim("ablo")} ${import_picocolors22.default.yellow("?")} ${import_picocolors22.default.dim(`couldn't ask which database Ablo reads (${state.detail})`)}
285622
+ ` ${import_picocolors23.default.dim("ablo")} ${import_picocolors23.default.yellow("?")} ${import_picocolors23.default.dim(`couldn't ask which database Ablo reads (${state.detail})`)}
285238
285623
  `
285239
285624
  );
285240
285625
  return;
285241
285626
  }
285242
285627
  if (state.kind === "none") {
285243
285628
  console.log(
285244
- ` ${import_picocolors22.default.dim("ablo")} ${import_picocolors22.default.yellow("!")} no database is registered for this plane, so Ablo does not read this one`
285629
+ ` ${import_picocolors23.default.dim("ablo")} ${import_picocolors23.default.yellow("!")} no database is registered for this plane, so Ablo does not read this one`
285245
285630
  );
285246
285631
  console.log(
285247
- ` ${import_picocolors22.default.dim(`Connect it with ${import_picocolors22.default.bold("ablo connect apply")}. Until then a table here is invisible to the engine.`)}
285632
+ ` ${import_picocolors23.default.dim(`Connect it with ${import_picocolors23.default.bold("ablo connect apply")}. Until then a table here is invisible to the engine.`)}
285248
285633
  `
285249
285634
  );
285250
285635
  return;
@@ -285252,15 +285637,15 @@ async function reportReadSubject(dbUrl) {
285252
285637
  const registered = [...new Set(state.hosts)];
285253
285638
  if (host && registered.length > 0 && !registered.includes(host)) {
285254
285639
  console.log(
285255
- ` ${import_picocolors22.default.dim("ablo")} ${import_picocolors22.default.yellow("!")} Ablo reads ${import_picocolors22.default.bold(registered.join(", "))}`
285640
+ ` ${import_picocolors23.default.dim("ablo")} ${import_picocolors23.default.yellow("!")} Ablo reads ${import_picocolors23.default.bold(registered.join(", "))}`
285256
285641
  );
285257
285642
  console.log(
285258
- ` ${import_picocolors22.default.dim("If that is this database under a pooled hostname, this is fine \u2014 otherwise the report below describes a database Ablo never reads.")}
285643
+ ` ${import_picocolors23.default.dim("If that is this database under a pooled hostname, this is fine \u2014 otherwise the report below describes a database Ablo never reads.")}
285259
285644
  `
285260
285645
  );
285261
285646
  return;
285262
285647
  }
285263
- console.log(` ${import_picocolors22.default.dim("ablo")} ${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.dim("reads this database")}
285648
+ console.log(` ${import_picocolors23.default.dim("ablo")} ${import_picocolors23.default.green("\u2713")} ${import_picocolors23.default.dim("reads this database")}
285264
285649
  `);
285265
285650
  }
285266
285651
  async function check(argv) {
@@ -285268,20 +285653,20 @@ async function check(argv) {
285268
285653
  try {
285269
285654
  args = parseCheckArgs(argv);
285270
285655
  } catch (err) {
285271
- console.error(import_picocolors22.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285656
+ console.error(import_picocolors23.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285272
285657
  process.exit(1);
285273
285658
  }
285274
285659
  const dbUrl = readProjectAdminDatabaseUrl();
285275
285660
  if (!dbUrl) {
285276
285661
  console.error(
285277
- import_picocolors22.default.red(` No database.`) + import_picocolors22.default.dim(` Set ${import_picocolors22.default.bold(ADMIN_URL_VAR)} to the Postgres you want Ablo to adopt.`)
285662
+ import_picocolors23.default.red(` No database.`) + import_picocolors23.default.dim(` Set ${import_picocolors23.default.bold(ADMIN_URL_VAR)} to the Postgres you want Ablo to adopt.`)
285278
285663
  );
285279
285664
  process.exit(1);
285280
285665
  }
285281
285666
  const schema = await loadSchema(args.schemaPath, args.exportName);
285282
285667
  const schemaJson = JSON.parse((0, import_schema9.serializeSchema)(schema));
285283
285668
  console.log(`
285284
- ${brand("ablo")} ${import_picocolors22.default.dim("check")} ${import_picocolors22.default.dim(`schema "${args.appSchema}"`)}
285669
+ ${brand("ablo")} ${import_picocolors23.default.dim("check")} ${import_picocolors23.default.dim(`schema "${args.appSchema}"`)}
285285
285670
  `);
285286
285671
  await reportReadSubject(dbUrl);
285287
285672
  const sql = src_default(dbUrl, { max: 1, prepare: false, onnotice: () => {
@@ -285293,7 +285678,7 @@ async function check(argv) {
285293
285678
  [args.appSchema]
285294
285679
  );
285295
285680
  } catch (err) {
285296
- console.error(import_picocolors22.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
285681
+ console.error(import_picocolors23.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
285297
285682
  await sql.end({ timeout: 2 });
285298
285683
  process.exit(1);
285299
285684
  }
@@ -285315,7 +285700,7 @@ async function check(argv) {
285315
285700
  declaredTables.add(table);
285316
285701
  const present = colsByTable.get(table);
285317
285702
  if (!present) {
285318
- console.log(` ${import_picocolors22.default.red("\u2717")} ${import_picocolors22.default.bold(key)} ${import_picocolors22.default.dim("\u2192")} table ${import_picocolors22.default.bold(table)} ${import_picocolors22.default.red("not found")}`);
285703
+ console.log(` ${import_picocolors23.default.red("\u2717")} ${import_picocolors23.default.bold(key)} ${import_picocolors23.default.dim("\u2192")} table ${import_picocolors23.default.bold(table)} ${import_picocolors23.default.red("not found")}`);
285319
285704
  errors++;
285320
285705
  continue;
285321
285706
  }
@@ -285337,26 +285722,26 @@ async function check(argv) {
285337
285722
  if (!present.has(col)) problems.push(`missing column "${col}" (field ${fieldName})`);
285338
285723
  }
285339
285724
  if (problems.length > 0) {
285340
- console.log(` ${import_picocolors22.default.red("\u2717")} ${import_picocolors22.default.bold(key)} ${import_picocolors22.default.dim("\u2192")} ${table}`);
285341
- for (const p2 of problems) console.log(` ${import_picocolors22.default.red("\u2022")} ${p2}`);
285342
- for (const w2 of warns) console.log(` ${import_picocolors22.default.yellow("\u2022")} ${w2}`);
285725
+ console.log(` ${import_picocolors23.default.red("\u2717")} ${import_picocolors23.default.bold(key)} ${import_picocolors23.default.dim("\u2192")} ${table}`);
285726
+ for (const p2 of problems) console.log(` ${import_picocolors23.default.red("\u2022")} ${p2}`);
285727
+ for (const w2 of warns) console.log(` ${import_picocolors23.default.yellow("\u2022")} ${w2}`);
285343
285728
  errors++;
285344
285729
  } else if (warns.length > 0) {
285345
- console.log(` ${import_picocolors22.default.yellow("!")} ${import_picocolors22.default.bold(key)} ${import_picocolors22.default.dim("\u2192")} ${table}`);
285346
- for (const w2 of warns) console.log(` ${import_picocolors22.default.yellow("\u2022")} ${w2}`);
285730
+ console.log(` ${import_picocolors23.default.yellow("!")} ${import_picocolors23.default.bold(key)} ${import_picocolors23.default.dim("\u2192")} ${table}`);
285731
+ for (const w2 of warns) console.log(` ${import_picocolors23.default.yellow("\u2022")} ${w2}`);
285347
285732
  warnings++;
285348
285733
  } else {
285349
- console.log(` ${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.bold(key)} ${import_picocolors22.default.dim(`\u2192 ${table} (id, ${orgCol ?? "no org"} ok)`)}`);
285734
+ console.log(` ${import_picocolors23.default.green("\u2713")} ${import_picocolors23.default.bold(key)} ${import_picocolors23.default.dim(`\u2192 ${table} (id, ${orgCol ?? "no org"} ok)`)}`);
285350
285735
  }
285351
285736
  }
285352
285737
  const modelCount = Object.keys(schemaJson.models).length;
285353
285738
  const ignored = [...colsByTable.keys()].filter((t) => !declaredTables.has(t)).length;
285354
285739
  console.log(
285355
285740
  `
285356
- ${modelCount} model${modelCount === 1 ? "" : "s"} \xB7 ${import_picocolors22.default.green(`${modelCount - errors - warnings} ok`)}` + (warnings ? ` \xB7 ${import_picocolors22.default.yellow(`${warnings} warning${warnings === 1 ? "" : "s"}`)}` : "") + (errors ? ` \xB7 ${import_picocolors22.default.red(`${errors} error${errors === 1 ? "" : "s"}`)}` : "")
285741
+ ${modelCount} model${modelCount === 1 ? "" : "s"} \xB7 ${import_picocolors23.default.green(`${modelCount - errors - warnings} ok`)}` + (warnings ? ` \xB7 ${import_picocolors23.default.yellow(`${warnings} warning${warnings === 1 ? "" : "s"}`)}` : "") + (errors ? ` \xB7 ${import_picocolors23.default.red(`${errors} error${errors === 1 ? "" : "s"}`)}` : "")
285357
285742
  );
285358
285743
  if (ignored > 0) {
285359
- console.log(` ${import_picocolors22.default.dim(`${ignored} other table${ignored === 1 ? "" : "s"} in your database \u2014 ignored by Ablo`)}`);
285744
+ console.log(` ${import_picocolors23.default.dim(`${ignored} other table${ignored === 1 ? "" : "s"} in your database \u2014 ignored by Ablo`)}`);
285360
285745
  }
285361
285746
  console.log();
285362
285747
  process.exit(errors > 0 ? 1 : 0);
@@ -285367,7 +285752,7 @@ init_dbRole();
285367
285752
 
285368
285753
  // src/upgrade.ts
285369
285754
  init_cjs_shims();
285370
- var import_picocolors23 = __toESM(require_picocolors(), 1);
285755
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
285371
285756
  var import_ts_morph = __toESM(require_ts_morph(), 1);
285372
285757
  var DEFAULT_GLOBS = ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}", "ablo/**/*.{ts,tsx}", "lib/**/*.{ts,tsx}"];
285373
285758
  var VERB_ARGS = {
@@ -285445,7 +285830,7 @@ async function upgrade(argv) {
285445
285830
  project.addSourceFilesAtPaths(globs.length > 0 ? globs : DEFAULT_GLOBS);
285446
285831
  const files = project.getSourceFiles();
285447
285832
  if (files.length === 0) {
285448
- console.log(import_picocolors23.default.yellow(' No .ts/.tsx files found. Pass a glob, e.g. `ablo upgrade "src/**/*.tsx"`.'));
285833
+ console.log(import_picocolors24.default.yellow(' No .ts/.tsx files found. Pass a glob, e.g. `ablo upgrade "src/**/*.tsx"`.'));
285449
285834
  return;
285450
285835
  }
285451
285836
  const edits = [];
@@ -285519,39 +285904,39 @@ async function upgrade(argv) {
285519
285904
  const rel = (f) => f.replace(cwd + "/", "");
285520
285905
  console.log();
285521
285906
  if (edits.length === 0 && manual.length === 0) {
285522
- console.log(import_picocolors23.default.green(" \u2713 Nothing to migrate \u2014 your code is already on the current API."));
285907
+ console.log(import_picocolors24.default.green(" \u2713 Nothing to migrate \u2014 your code is already on the current API."));
285523
285908
  return;
285524
285909
  }
285525
285910
  if (edits.length > 0) {
285526
- console.log(import_picocolors23.default.bold(` ${write ? "Applied" : "Would apply"} ${edits.length} change${edits.length === 1 ? "" : "s"}:`));
285911
+ console.log(import_picocolors24.default.bold(` ${write ? "Applied" : "Would apply"} ${edits.length} change${edits.length === 1 ? "" : "s"}:`));
285527
285912
  for (const e2 of edits) {
285528
- console.log(` ${import_picocolors23.default.dim(`${rel(e2.file)}:${e2.line}`)} ${import_picocolors23.default.cyan(e2.rule)}`);
285529
- console.log(` ${import_picocolors23.default.red("-")} ${e2.before}`);
285530
- console.log(` ${import_picocolors23.default.green("+")} ${e2.after}`);
285913
+ console.log(` ${import_picocolors24.default.dim(`${rel(e2.file)}:${e2.line}`)} ${import_picocolors24.default.cyan(e2.rule)}`);
285914
+ console.log(` ${import_picocolors24.default.red("-")} ${e2.before}`);
285915
+ console.log(` ${import_picocolors24.default.green("+")} ${e2.after}`);
285531
285916
  }
285532
285917
  }
285533
285918
  if (manual.length > 0) {
285534
285919
  console.log();
285535
- console.log(import_picocolors23.default.bold(import_picocolors23.default.yellow(` ${manual.length} spot${manual.length === 1 ? "" : "s"} need manual review (structural):`)));
285920
+ console.log(import_picocolors24.default.bold(import_picocolors24.default.yellow(` ${manual.length} spot${manual.length === 1 ? "" : "s"} need manual review (structural):`)));
285536
285921
  for (const m2 of manual) {
285537
- console.log(` ${import_picocolors23.default.dim(`${rel(m2.file)}:${m2.line}`)} ${import_picocolors23.default.yellow(m2.rule)}`);
285538
- console.log(` ${import_picocolors23.default.dim(m2.snippet)}`);
285922
+ console.log(` ${import_picocolors24.default.dim(`${rel(m2.file)}:${m2.line}`)} ${import_picocolors24.default.yellow(m2.rule)}`);
285923
+ console.log(` ${import_picocolors24.default.dim(m2.snippet)}`);
285539
285924
  console.log(` \u2192 ${m2.hint}`);
285540
285925
  }
285541
285926
  }
285542
285927
  console.log();
285543
285928
  if (write) {
285544
285929
  await project.save();
285545
- console.log(import_picocolors23.default.green(` \u2713 Wrote ${edits.length} change${edits.length === 1 ? "" : "s"}. Review the diff, run your typecheck.`));
285930
+ console.log(import_picocolors24.default.green(` \u2713 Wrote ${edits.length} change${edits.length === 1 ? "" : "s"}. Review the diff, run your typecheck.`));
285546
285931
  } else {
285547
- console.log(import_picocolors23.default.dim(" Dry run. Re-run with `--write` to apply the auto-fixes above (manual items are never auto-written)."));
285932
+ console.log(import_picocolors24.default.dim(" Dry run. Re-run with `--write` to apply the auto-fixes above (manual items are never auto-written)."));
285548
285933
  }
285549
285934
  }
285550
285935
 
285551
285936
  // src/pull.ts
285552
285937
  init_cjs_shims();
285553
- var import_errors17 = require("@abloatai/transaction/errors");
285554
- var import_picocolors24 = __toESM(require_picocolors(), 1);
285938
+ var import_errors18 = require("@abloatai/transaction/errors");
285939
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
285555
285940
  init_src();
285556
285941
  var import_fs10 = require("fs");
285557
285942
  init_theme();
@@ -285579,7 +285964,7 @@ function parsePullArgs(argv) {
285579
285964
  force = true;
285580
285965
  break;
285581
285966
  default:
285582
- throw new import_errors17.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285967
+ throw new import_errors18.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285583
285968
  }
285584
285969
  }
285585
285970
  return { out, appSchema, importPath, force };
@@ -285649,56 +286034,56 @@ async function pull(argv) {
285649
286034
  try {
285650
286035
  args = parsePullArgs(argv);
285651
286036
  } catch (err) {
285652
- console.error(import_picocolors24.default.red(` ${err instanceof Error ? err.message : String(err)}`));
286037
+ console.error(import_picocolors25.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285653
286038
  process.exit(1);
285654
286039
  }
285655
286040
  const dbUrl = readProjectAdminDatabaseUrl();
285656
286041
  if (!dbUrl) {
285657
286042
  console.error(
285658
- import_picocolors24.default.red(` No database.`) + import_picocolors24.default.dim(` Set ${import_picocolors24.default.bold(ADMIN_URL_VAR)} to the Postgres to pull from.`)
286043
+ import_picocolors25.default.red(` No database.`) + import_picocolors25.default.dim(` Set ${import_picocolors25.default.bold(ADMIN_URL_VAR)} to the Postgres to pull from.`)
285659
286044
  );
285660
286045
  process.exit(1);
285661
286046
  }
285662
286047
  if ((0, import_fs10.existsSync)(args.out) && !args.force) {
285663
286048
  console.error(
285664
- import_picocolors24.default.red(` ${args.out} already exists.`) + import_picocolors24.default.dim(` Re-run with ${import_picocolors24.default.bold("--force")} to overwrite.`)
286049
+ import_picocolors25.default.red(` ${args.out} already exists.`) + import_picocolors25.default.dim(` Re-run with ${import_picocolors25.default.bold("--force")} to overwrite.`)
285665
286050
  );
285666
286051
  process.exit(1);
285667
286052
  }
285668
286053
  console.log(`
285669
- ${brand("ablo")} ${import_picocolors24.default.dim("pull")} ${import_picocolors24.default.dim(`schema "${args.appSchema}"`)}
286054
+ ${brand("ablo")} ${import_picocolors25.default.dim("pull")} ${import_picocolors25.default.dim(`schema "${args.appSchema}"`)}
285670
286055
  `);
285671
286056
  let result;
285672
286057
  try {
285673
286058
  result = await buildSchemaSourceFromDb({ dbUrl, appSchema: args.appSchema, importPath: args.importPath });
285674
286059
  } catch (err) {
285675
- console.error(import_picocolors24.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
286060
+ console.error(import_picocolors25.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
285676
286061
  process.exit(1);
285677
286062
  }
285678
286063
  if (result.models.length === 0) {
285679
286064
  console.error(
285680
- import_picocolors24.default.yellow(` No adoptable tables found`) + import_picocolors24.default.dim(` (a model needs an ${import_picocolors24.default.bold("id")} + ${import_picocolors24.default.bold("organization_id")} column).`)
286065
+ import_picocolors25.default.yellow(` No adoptable tables found`) + import_picocolors25.default.dim(` (a model needs an ${import_picocolors25.default.bold("id")} + ${import_picocolors25.default.bold("organization_id")} column).`)
285681
286066
  );
285682
286067
  process.exit(1);
285683
286068
  }
285684
286069
  (0, import_fs10.writeFileSync)(args.out, result.source);
285685
- console.log(` ${import_picocolors24.default.green("\u2713")} wrote ${import_picocolors24.default.bold(args.out)} ${import_picocolors24.default.dim(`(${result.models.length} models)`)}`);
285686
- console.log(` ${import_picocolors24.default.dim(`models: ${result.models.join(", ")}`)}`);
286070
+ console.log(` ${import_picocolors25.default.green("\u2713")} wrote ${import_picocolors25.default.bold(args.out)} ${import_picocolors25.default.dim(`(${result.models.length} models)`)}`);
286071
+ console.log(` ${import_picocolors25.default.dim(`models: ${result.models.join(", ")}`)}`);
285687
286072
  if (result.skipped.length > 0) {
285688
- console.log(` ${import_picocolors24.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
285689
- for (const s of result.skipped) console.log(` ${import_picocolors24.default.dim(`- ${s.name}: ${s.reason}`)}`);
286073
+ console.log(` ${import_picocolors25.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
286074
+ for (const s of result.skipped) console.log(` ${import_picocolors25.default.dim(`- ${s.name}: ${s.reason}`)}`);
285690
286075
  }
285691
286076
  console.log(
285692
286077
  `
285693
- ${import_picocolors24.default.dim("Introspection is lossy (enums, JSON shape, relations). Review the file, then")} ${import_picocolors24.default.bold("ablo check")}.
286078
+ ${import_picocolors25.default.dim("Introspection is lossy (enums, JSON shape, relations). Review the file, then")} ${import_picocolors25.default.bold("ablo check")}.
285694
286079
  `
285695
286080
  );
285696
286081
  }
285697
286082
 
285698
286083
  // src/prismaPull.ts
285699
286084
  init_cjs_shims();
285700
- var import_errors18 = require("@abloatai/transaction/errors");
285701
- var import_picocolors25 = __toESM(require_picocolors(), 1);
286085
+ var import_errors19 = require("@abloatai/transaction/errors");
286086
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
285702
286087
  var import_fs11 = require("fs");
285703
286088
  init_theme();
285704
286089
  var DEFAULT_SCHEMA = "prisma/schema.prisma";
@@ -285895,7 +286280,7 @@ function parsePrismaPullArgs(argv) {
285895
286280
  force = true;
285896
286281
  break;
285897
286282
  default:
285898
- if (arg.startsWith("--")) throw new import_errors18.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
286283
+ if (arg.startsWith("--")) throw new import_errors19.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285899
286284
  schema = arg;
285900
286285
  }
285901
286286
  }
@@ -285906,56 +286291,56 @@ async function prismaPull(argv) {
285906
286291
  try {
285907
286292
  args = parsePrismaPullArgs(argv);
285908
286293
  } catch (err) {
285909
- console.error(import_picocolors25.default.red(` ${err instanceof Error ? err.message : String(err)}`));
286294
+ console.error(import_picocolors26.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285910
286295
  process.exit(1);
285911
286296
  }
285912
286297
  if (!(0, import_fs11.existsSync)(args.schema)) {
285913
286298
  console.error(
285914
- import_picocolors25.default.red(` No Prisma schema at ${import_picocolors25.default.bold(args.schema)}.`) + import_picocolors25.default.dim(` Pass a path: ${import_picocolors25.default.bold("ablo pull prisma <path>")}.`)
286299
+ import_picocolors26.default.red(` No Prisma schema at ${import_picocolors26.default.bold(args.schema)}.`) + import_picocolors26.default.dim(` Pass a path: ${import_picocolors26.default.bold("ablo pull prisma <path>")}.`)
285915
286300
  );
285916
286301
  process.exit(1);
285917
286302
  }
285918
286303
  if ((0, import_fs11.existsSync)(args.out) && !args.force) {
285919
286304
  console.error(
285920
- import_picocolors25.default.red(` ${args.out} already exists.`) + import_picocolors25.default.dim(` Re-run with ${import_picocolors25.default.bold("--force")} to overwrite.`)
286305
+ import_picocolors26.default.red(` ${args.out} already exists.`) + import_picocolors26.default.dim(` Re-run with ${import_picocolors26.default.bold("--force")} to overwrite.`)
285921
286306
  );
285922
286307
  process.exit(1);
285923
286308
  }
285924
286309
  console.log(`
285925
- ${brand("ablo")} ${import_picocolors25.default.dim("pull prisma")} ${import_picocolors25.default.dim(args.schema)}
286310
+ ${brand("ablo")} ${import_picocolors26.default.dim("pull prisma")} ${import_picocolors26.default.dim(args.schema)}
285926
286311
  `);
285927
286312
  let result;
285928
286313
  try {
285929
286314
  const src = (0, import_fs11.readFileSync)(args.schema, "utf8");
285930
286315
  result = buildSchemaSourceFromPrisma({ src, importPath: args.importPath });
285931
286316
  } catch (err) {
285932
- console.error(import_picocolors25.default.red(` Couldn't parse the schema: ${err instanceof Error ? err.message : String(err)}`));
286317
+ console.error(import_picocolors26.default.red(` Couldn't parse the schema: ${err instanceof Error ? err.message : String(err)}`));
285933
286318
  process.exit(1);
285934
286319
  }
285935
286320
  if (result.models.length === 0) {
285936
286321
  console.error(
285937
- import_picocolors25.default.yellow(` No adoptable models found`) + import_picocolors25.default.dim(` (a model needs an ${import_picocolors25.default.bold("id")} + ${import_picocolors25.default.bold("organizationId")} / ${import_picocolors25.default.bold("organization_id")}).`)
286322
+ import_picocolors26.default.yellow(` No adoptable models found`) + import_picocolors26.default.dim(` (a model needs an ${import_picocolors26.default.bold("id")} + ${import_picocolors26.default.bold("organizationId")} / ${import_picocolors26.default.bold("organization_id")}).`)
285938
286323
  );
285939
286324
  process.exit(1);
285940
286325
  }
285941
286326
  (0, import_fs11.writeFileSync)(args.out, result.source);
285942
- console.log(` ${import_picocolors25.default.green("\u2713")} wrote ${import_picocolors25.default.bold(args.out)} ${import_picocolors25.default.dim(`(${result.models.length} models)`)}`);
285943
- console.log(` ${import_picocolors25.default.dim(`models: ${result.models.join(", ")}`)}`);
286327
+ console.log(` ${import_picocolors26.default.green("\u2713")} wrote ${import_picocolors26.default.bold(args.out)} ${import_picocolors26.default.dim(`(${result.models.length} models)`)}`);
286328
+ console.log(` ${import_picocolors26.default.dim(`models: ${result.models.join(", ")}`)}`);
285944
286329
  if (result.skipped.length > 0) {
285945
- console.log(` ${import_picocolors25.default.dim(`${result.skipped.length} model(s) skipped:`)}`);
285946
- for (const s of result.skipped) console.log(` ${import_picocolors25.default.dim(`- ${s.name}: ${s.reason}`)}`);
286330
+ console.log(` ${import_picocolors26.default.dim(`${result.skipped.length} model(s) skipped:`)}`);
286331
+ for (const s of result.skipped) console.log(` ${import_picocolors26.default.dim(`- ${s.name}: ${s.reason}`)}`);
285947
286332
  }
285948
286333
  console.log(
285949
286334
  `
285950
- ${import_picocolors25.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors25.default.bold("ablo check")}.
286335
+ ${import_picocolors26.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors26.default.bold("ablo check")}.
285951
286336
  `
285952
286337
  );
285953
286338
  }
285954
286339
 
285955
286340
  // src/drizzlePull.ts
285956
286341
  init_cjs_shims();
285957
- var import_picocolors26 = __toESM(require_picocolors(), 1);
285958
- var import_errors19 = require("@abloatai/transaction/errors");
286342
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
286343
+ var import_errors20 = require("@abloatai/transaction/errors");
285959
286344
  var import_fs12 = require("fs");
285960
286345
  var import_path7 = require("path");
285961
286346
  init_theme();
@@ -286062,7 +286447,7 @@ function parseDrizzlePullArgs(argv) {
286062
286447
  force = true;
286063
286448
  break;
286064
286449
  default:
286065
- if (arg.startsWith("--")) throw new import_errors19.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
286450
+ if (arg.startsWith("--")) throw new import_errors20.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
286066
286451
  schema = arg;
286067
286452
  }
286068
286453
  }
@@ -286079,27 +286464,27 @@ async function drizzlePull(argv) {
286079
286464
  try {
286080
286465
  args = parseDrizzlePullArgs(argv);
286081
286466
  } catch (err) {
286082
- console.error(import_picocolors26.default.red(` ${err instanceof Error ? err.message : String(err)}`));
286467
+ console.error(import_picocolors27.default.red(` ${err instanceof Error ? err.message : String(err)}`));
286083
286468
  process.exit(1);
286084
286469
  }
286085
286470
  if (!args.schema) {
286086
286471
  console.error(
286087
- import_picocolors26.default.red(` No Drizzle schema given.`) + import_picocolors26.default.dim(` Pass the module: ${import_picocolors26.default.bold("ablo pull drizzle src/db/schema.ts")}.`)
286472
+ import_picocolors27.default.red(` No Drizzle schema given.`) + import_picocolors27.default.dim(` Pass the module: ${import_picocolors27.default.bold("ablo pull drizzle src/db/schema.ts")}.`)
286088
286473
  );
286089
286474
  process.exit(1);
286090
286475
  }
286091
286476
  if (!(0, import_fs12.existsSync)(args.schema)) {
286092
- console.error(import_picocolors26.default.red(` No file at ${import_picocolors26.default.bold(args.schema)}.`));
286477
+ console.error(import_picocolors27.default.red(` No file at ${import_picocolors27.default.bold(args.schema)}.`));
286093
286478
  process.exit(1);
286094
286479
  }
286095
286480
  if ((0, import_fs12.existsSync)(args.out) && !args.force) {
286096
286481
  console.error(
286097
- import_picocolors26.default.red(` ${args.out} already exists.`) + import_picocolors26.default.dim(` Re-run with ${import_picocolors26.default.bold("--force")} to overwrite.`)
286482
+ import_picocolors27.default.red(` ${args.out} already exists.`) + import_picocolors27.default.dim(` Re-run with ${import_picocolors27.default.bold("--force")} to overwrite.`)
286098
286483
  );
286099
286484
  process.exit(1);
286100
286485
  }
286101
286486
  console.log(`
286102
- ${brand("ablo")} ${import_picocolors26.default.dim("pull drizzle")} ${import_picocolors26.default.dim(args.schema)}
286487
+ ${brand("ablo")} ${import_picocolors27.default.dim("pull drizzle")} ${import_picocolors27.default.dim(args.schema)}
286103
286488
  `);
286104
286489
  let result;
286105
286490
  try {
@@ -286107,26 +286492,26 @@ async function drizzlePull(argv) {
286107
286492
  result = await buildSchemaSourceFromDrizzle({ mod, importPath: args.importPath });
286108
286493
  } catch (err) {
286109
286494
  const msg = err instanceof Error ? err.message : String(err);
286110
- const hint = msg.includes("Cannot find package 'drizzle-orm'") ? import_picocolors26.default.dim(` (install ${import_picocolors26.default.bold("drizzle-orm")} in this project)`) : "";
286111
- console.error(import_picocolors26.default.red(` Couldn't load the schema: ${msg}`) + hint);
286495
+ const hint = msg.includes("Cannot find package 'drizzle-orm'") ? import_picocolors27.default.dim(` (install ${import_picocolors27.default.bold("drizzle-orm")} in this project)`) : "";
286496
+ console.error(import_picocolors27.default.red(` Couldn't load the schema: ${msg}`) + hint);
286112
286497
  process.exit(1);
286113
286498
  }
286114
286499
  if (result.models.length === 0) {
286115
286500
  console.error(
286116
- import_picocolors26.default.yellow(` No adoptable tables found`) + import_picocolors26.default.dim(` (a table needs an ${import_picocolors26.default.bold("id")} + ${import_picocolors26.default.bold("organization_id")} column).`)
286501
+ import_picocolors27.default.yellow(` No adoptable tables found`) + import_picocolors27.default.dim(` (a table needs an ${import_picocolors27.default.bold("id")} + ${import_picocolors27.default.bold("organization_id")} column).`)
286117
286502
  );
286118
286503
  process.exit(1);
286119
286504
  }
286120
286505
  (0, import_fs12.writeFileSync)(args.out, result.source);
286121
- console.log(` ${import_picocolors26.default.green("\u2713")} wrote ${import_picocolors26.default.bold(args.out)} ${import_picocolors26.default.dim(`(${result.models.length} models)`)}`);
286122
- console.log(` ${import_picocolors26.default.dim(`models: ${result.models.join(", ")}`)}`);
286506
+ console.log(` ${import_picocolors27.default.green("\u2713")} wrote ${import_picocolors27.default.bold(args.out)} ${import_picocolors27.default.dim(`(${result.models.length} models)`)}`);
286507
+ console.log(` ${import_picocolors27.default.dim(`models: ${result.models.join(", ")}`)}`);
286123
286508
  if (result.skipped.length > 0) {
286124
- console.log(` ${import_picocolors26.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
286125
- for (const s of result.skipped) console.log(` ${import_picocolors26.default.dim(`- ${s.name}: ${s.reason}`)}`);
286509
+ console.log(` ${import_picocolors27.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
286510
+ for (const s of result.skipped) console.log(` ${import_picocolors27.default.dim(`- ${s.name}: ${s.reason}`)}`);
286126
286511
  }
286127
286512
  console.log(
286128
286513
  `
286129
- ${import_picocolors26.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors26.default.bold("ablo check")}.
286514
+ ${import_picocolors27.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors27.default.bold("ablo check")}.
286130
286515
  `
286131
286516
  );
286132
286517
  }
@@ -286212,7 +286597,7 @@ async function getCurrentUser(): Promise<{ id: string } | null> {
286212
286597
 
286213
286598
  // src/index.ts
286214
286599
  var LOGO = `
286215
- ${brand("ablo")} ${import_picocolors27.default.dim("sync engine")}
286600
+ ${brand("ablo")} ${import_picocolors28.default.dim("sync engine")}
286216
286601
  `;
286217
286602
  var HANDLERS = {
286218
286603
  init: (argv) => init([...argv]),
@@ -286221,6 +286606,7 @@ var HANDLERS = {
286221
286606
  projects: (argv) => projects([...argv]),
286222
286607
  branch: (argv) => branches([...argv]),
286223
286608
  status: (argv) => status([...argv]),
286609
+ whoami: (argv) => whoami([...argv]),
286224
286610
  doctor: () => doctor(),
286225
286611
  logs: (argv) => logs([...argv]),
286226
286612
  webhooks: (argv) => webhooks([...argv]),
@@ -286239,7 +286625,7 @@ async function runDev(argv) {
286239
286625
  const devArgs = [...argv];
286240
286626
  const oneShot = devArgs.includes("--no-watch");
286241
286627
  console.log(
286242
- import_picocolors27.default.dim(
286628
+ import_picocolors28.default.dim(
286243
286629
  oneShot ? " `ablo dev --no-watch` prepares this Git branch and pushes once." : " `ablo dev` prepares this Git branch and watches the schema."
286244
286630
  )
286245
286631
  );
@@ -286253,31 +286639,11 @@ async function runPull(argv) {
286253
286639
  }
286254
286640
  async function runPush2(argv) {
286255
286641
  const rest = [...argv];
286256
- const advanced = rest.some(
286257
- (a) => ["--force", "--rename", "--backfill", "--url", "--dry-run", "--plan", "--yes", "-y", "--allow-dirty"].includes(a)
286258
- );
286259
- const watching = rest.includes("--watch");
286260
- const guard = guardActiveProjectKey();
286261
- if (!guard.ok && guard.available.length > 0 && !rest.includes("--url")) {
286262
- console.error(
286263
- ` ${import_picocolors27.default.yellow("\u26A0")} active project ${import_picocolors27.default.bold(guard.activeProfile)} has no stored key ${import_picocolors27.default.dim(
286264
- `(you have keys for: ${guard.available.join(", ")})`
286265
- )}`
286266
- );
286267
- const loginCmd = guard.activeProfile === "default" ? "ablo login" : `ablo login --project ${guard.activeProfile}`;
286268
- console.error(
286269
- import_picocolors27.default.dim(` Mint one with ${import_picocolors27.default.bold(loginCmd)}, or switch with ${import_picocolors27.default.bold("ablo projects use <slug>")}.`)
286270
- );
286271
- process.exitCode = 1;
286272
- return;
286273
- }
286274
- const plan = resolvePushPlan();
286275
- if (advanced || plan.flow === "production" && !watching) await push(rest);
286276
- else await dev(rest);
286642
+ await push(rest);
286277
286643
  }
286278
286644
  function runRenamedSchema(argv) {
286279
286645
  const forwarded = argv.slice(1).join(" ");
286280
- console.error(` ${import_picocolors27.default.red("\u2717")} \`ablo schema push\` was renamed to \`${brand("ablo push")}\`.`);
286646
+ console.error(` ${import_picocolors28.default.red("\u2717")} \`ablo schema push\` was renamed to \`${brand("ablo push")}\`.`);
286281
286647
  console.error(` Run \`ablo push${forwarded ? " " + forwarded : ""}\` instead.`);
286282
286648
  process.exitCode = 1;
286283
286649
  }
@@ -286287,7 +286653,7 @@ async function main() {
286287
286653
  const argv = process.argv.slice(3);
286288
286654
  if (!command && raw !== void 0 && raw !== "help" && !raw.startsWith("-")) {
286289
286655
  const suggestion = suggestCommand(raw);
286290
- throw new import_errors20.AbloValidationError(
286656
+ throw new import_errors21.AbloValidationError(
286291
286657
  `\`${raw}\` isn't an ablo command.` + (suggestion ? ` Did you mean \`ablo ${suggestion}\`?` : " Run `ablo help --all` to see every command."),
286292
286658
  { code: "cli_invalid_arguments" }
286293
286659
  );
@@ -286318,7 +286684,7 @@ function printCoreHelp() {
286318
286684
  const width = Math.max(...rows.map((r2) => r2.run.length)) + 4;
286319
286685
  console.log(LOGO);
286320
286686
  for (const group of CORE_GROUPS) {
286321
- console.log(` ${import_picocolors27.default.bold(group)}`);
286687
+ console.log(` ${import_picocolors28.default.bold(group)}`);
286322
286688
  const printed = group === "More" ? [...coreRows(group), ...extra] : coreRows(group);
286323
286689
  for (const row of printed) console.log(` npx ablo ${row.run.padEnd(width)}${row.does}`);
286324
286690
  console.log();
@@ -286327,7 +286693,7 @@ function printCoreHelp() {
286327
286693
  }
286328
286694
  function printSchemaReminder() {
286329
286695
  console.log(
286330
- import_picocolors27.default.dim(` Edit ${import_picocolors27.default.bold("ablo/schema.ts")}, then push \u2014 writes to models you haven't pushed fail with `) + import_picocolors27.default.yellow("server_execute_unknown_model") + import_picocolors27.default.dim(".")
286696
+ import_picocolors28.default.dim(` Edit ${import_picocolors28.default.bold("ablo/schema.ts")}, then push \u2014 writes to models you haven't pushed fail with `) + import_picocolors28.default.yellow("server_execute_unknown_model") + import_picocolors28.default.dim(".")
286331
286697
  );
286332
286698
  console.log();
286333
286699
  }
@@ -286337,7 +286703,7 @@ function printFullHelp() {
286337
286703
  ) + 2;
286338
286704
  console.log(LOGO);
286339
286705
  for (const group of FULL_GROUPS) {
286340
- console.log(` ${import_picocolors27.default.bold(group)}`);
286706
+ console.log(` ${import_picocolors28.default.bold(group)}`);
286341
286707
  for (const row of fullRows(group)) {
286342
286708
  console.log(row.does === void 0 ? ` ${" ".repeat(9)}${row.run}` : ` npx ablo ${row.run.padEnd(width)}${row.does}`);
286343
286709
  }
@@ -286392,7 +286758,7 @@ async function ensureInitProject(opts) {
286392
286758
  const ensured = await ensureProject(slug);
286393
286759
  if (ensured) {
286394
286760
  console.log(
286395
- ` ${import_picocolors27.default.green("\u2713")} ${ensured.created ? "Created" : "Using"} project ${import_picocolors27.default.bold(ensured.slug)} ${import_picocolors27.default.dim(`(${ensured.id})`)} \u2014 keys you mint for it are isolated from the org's other apps.`
286761
+ ` ${import_picocolors28.default.green("\u2713")} ${ensured.created ? "Created" : "Using"} project ${import_picocolors28.default.bold(ensured.slug)} ${import_picocolors28.default.dim(`(${ensured.id})`)} \u2014 keys you mint for it are isolated from the org's other apps.`
286396
286762
  );
286397
286763
  }
286398
286764
  }
@@ -286434,7 +286800,7 @@ async function chooseBool(flagValue, fallback, interactive, prompt) {
286434
286800
  async function init(args = []) {
286435
286801
  const opts = parseInitArgs(args);
286436
286802
  const interactive = Boolean(process.stdin.isTTY) && !opts.yes && !process.env.CI;
286437
- Ie(`${brand("ablo")} ${import_picocolors27.default.dim("sync engine")}`);
286803
+ Ie(`${brand("ablo")} ${import_picocolors28.default.dim("sync engine")}`);
286438
286804
  if (!(0, import_fs13.existsSync)("package.json")) {
286439
286805
  xe("No package.json found. Run this from your project root.");
286440
286806
  process.exit(1);
@@ -286512,7 +286878,7 @@ async function init(args = []) {
286512
286878
  if (pullExisting) {
286513
286879
  const dbUrl = readProjectAdminDatabaseUrl();
286514
286880
  if (!dbUrl) {
286515
- schemaNote = import_picocolors27.default.dim(` (no ${ADMIN_URL_VAR} \u2014 wrote starter; run \`ablo pull\` later)`);
286881
+ schemaNote = import_picocolors28.default.dim(` (no ${ADMIN_URL_VAR} \u2014 wrote starter; run \`ablo pull\` later)`);
286516
286882
  } else {
286517
286883
  try {
286518
286884
  const pulled = await buildSchemaSourceFromDb({
@@ -286522,12 +286888,12 @@ async function init(args = []) {
286522
286888
  });
286523
286889
  if (pulled.models.length > 0) {
286524
286890
  schemaSource = pulled.source;
286525
- schemaNote = import_picocolors27.default.dim(` (pulled ${pulled.models.length} models)`);
286891
+ schemaNote = import_picocolors28.default.dim(` (pulled ${pulled.models.length} models)`);
286526
286892
  } else {
286527
- schemaNote = import_picocolors27.default.dim(" (no adoptable tables \u2014 wrote starter)");
286893
+ schemaNote = import_picocolors28.default.dim(" (no adoptable tables \u2014 wrote starter)");
286528
286894
  }
286529
286895
  } catch {
286530
- schemaNote = import_picocolors27.default.dim(" (pull failed \u2014 wrote starter)");
286896
+ schemaNote = import_picocolors28.default.dim(" (pull failed \u2014 wrote starter)");
286531
286897
  }
286532
286898
  }
286533
286899
  }
@@ -286551,9 +286917,9 @@ async function init(args = []) {
286551
286917
  const existing = (0, import_fs13.readFileSync)(envFile, "utf-8");
286552
286918
  if (!existing.includes("ABLO_")) {
286553
286919
  (0, import_fs13.writeFileSync)(envFile, existing + "\n" + envBody);
286554
- created.push(`${envFile} ${import_picocolors27.default.dim("(appended)")}`);
286920
+ created.push(`${envFile} ${import_picocolors28.default.dim("(appended)")}`);
286555
286921
  } else {
286556
- created.push(`${envFile} ${import_picocolors27.default.dim("(already configured)")}`);
286922
+ created.push(`${envFile} ${import_picocolors28.default.dim("(already configured)")}`);
286557
286923
  }
286558
286924
  }
286559
286925
  if (agent) {
@@ -286569,17 +286935,17 @@ async function init(args = []) {
286569
286935
  }
286570
286936
  const providersPath = (0, import_path8.join)(layout.appBase, "providers.tsx");
286571
286937
  (0, import_fs13.writeFileSync)(providersPath, generateProviders());
286572
- created.push(`${providersPath} ${import_picocolors27.default.dim(`(wrap ${(0, import_path8.join)(layout.appBase, "layout.tsx")} in <Providers>)`)}`);
286938
+ created.push(`${providersPath} ${import_picocolors28.default.dim(`(wrap ${(0, import_path8.join)(layout.appBase, "layout.tsx")} in <Providers>)`)}`);
286573
286939
  const sessionDir = (0, import_path8.join)(layout.appBase, "api", "ablo-session");
286574
286940
  (0, import_fs13.mkdirSync)(sessionDir, { recursive: true });
286575
286941
  (0, import_fs13.writeFileSync)((0, import_path8.join)(sessionDir, "route.ts"), generateSessionRoute());
286576
- created.push(`${(0, import_path8.join)(sessionDir, "route.ts")} ${import_picocolors27.default.dim("(wire your auth)")}`);
286942
+ created.push(`${(0, import_path8.join)(sessionDir, "route.ts")} ${import_picocolors28.default.dim("(wire your auth)")}`);
286577
286943
  }
286578
286944
  if (framework !== "vanilla") {
286579
286945
  (0, import_fs13.writeFileSync)((0, import_path8.join)(abloDir, "TaskList.tsx"), generateComponent());
286580
286946
  created.push(`${abloDir}/TaskList.tsx`);
286581
286947
  }
286582
- Me(created.map((f) => `${import_picocolors27.default.green("\u2713")} ${f}`).join("\n"), "Created");
286948
+ Me(created.map((f) => `${import_picocolors28.default.green("\u2713")} ${f}`).join("\n"), "Created");
286583
286949
  const pm = detectPackageManager();
286584
286950
  if (opts.install) {
286585
286951
  const s = Y2();
@@ -286588,46 +286954,46 @@ async function init(args = []) {
286588
286954
  (0, import_child_process3.execSync)(`${pm} add @abloatai/ablo`, { stdio: "ignore" });
286589
286955
  s.stop("Installed @abloatai/ablo");
286590
286956
  } catch {
286591
- s.stop(`${import_picocolors27.default.yellow("!")} Couldn't auto-install \u2014 run ${import_picocolors27.default.bold(`${pm} install @abloatai/ablo`)}`);
286957
+ s.stop(`${import_picocolors28.default.yellow("!")} Couldn't auto-install \u2014 run ${import_picocolors28.default.bold(`${pm} install @abloatai/ablo`)}`);
286592
286958
  }
286593
286959
  }
286594
286960
  const steps = [
286595
- `Run ${import_picocolors27.default.bold("npx ablo login")} to authorize branch management`,
286596
- `Set ${import_picocolors27.default.bold("DATABASE_URL")} in ${import_picocolors27.default.bold(envFile)} \u2014 your Postgres is the system of record; rows live there, never with Ablo`,
286597
- `Run ${import_picocolors27.default.bold("npx ablo dev")} \u2014 pushes your schema definition and watches for changes`,
286961
+ `Run ${import_picocolors28.default.bold("npx ablo login")} to authorize branch management`,
286962
+ `Set ${import_picocolors28.default.bold("DATABASE_URL")} in ${import_picocolors28.default.bold(envFile)} \u2014 your Postgres is the system of record; rows live there, never with Ablo`,
286963
+ `Run ${import_picocolors28.default.bold("npx ablo dev")} \u2014 pushes your schema definition and watches for changes`,
286598
286964
  ...storage === "replication" ? [
286599
- `Connect your database \u2014 ${import_picocolors27.default.bold("npx ablo connect")} prints the one-time logical-replication setup SQL to run on your Postgres`,
286600
- `Verify it \u2014 ${import_picocolors27.default.bold("npx ablo connect check")} walks wal_level, the publication, the role, and replica identity, with the exact fix for anything missing`,
286601
- `Register it \u2014 ${import_picocolors27.default.bold("npx ablo connect register")} tells Ablo to start replicating; your app keeps writing through your own backend while Ablo tails the WAL`
286965
+ `Connect your database \u2014 ${import_picocolors28.default.bold("npx ablo connect")} prints the one-time logical-replication setup SQL to run on your Postgres`,
286966
+ `Verify it \u2014 ${import_picocolors28.default.bold("npx ablo connect check")} walks wal_level, the publication, the role, and replica identity, with the exact fix for anything missing`,
286967
+ `Register it \u2014 ${import_picocolors28.default.bold("npx ablo connect register")} tells Ablo to start replicating; your app keeps writing through your own backend while Ablo tails the WAL`
286602
286968
  ] : [
286603
- `Provision your DB: ${import_picocolors27.default.bold("npx ablo migrate")} (creates your Ablo-model tables + the adapter tables; keep your own migrations for everything else), then mount ${import_picocolors27.default.bold(`${abloDir}/data-source.ts`)} at ${import_picocolors27.default.bold("/api/ablo/source")}`
286969
+ `Provision your DB: ${import_picocolors28.default.bold("npx ablo migrate")} (creates your Ablo-model tables + the adapter tables; keep your own migrations for everything else), then mount ${import_picocolors28.default.bold(`${abloDir}/data-source.ts`)} at ${import_picocolors28.default.bold("/api/ablo/source")}`
286604
286970
  ],
286605
286971
  ...framework === "nextjs" ? [
286606
- `Wrap ${import_picocolors27.default.bold((0, import_path8.join)(layout.appBase, "layout.tsx"))} in ${import_picocolors27.default.bold("<Providers>")} (${(0, import_path8.join)(layout.appBase, "providers.tsx")}) and add your auth to ${import_picocolors27.default.bold((0, import_path8.join)(layout.appBase, "api", "ablo-session", "route.ts"))}`
286972
+ `Wrap ${import_picocolors28.default.bold((0, import_path8.join)(layout.appBase, "layout.tsx"))} in ${import_picocolors28.default.bold("<Providers>")} (${(0, import_path8.join)(layout.appBase, "providers.tsx")}) and add your auth to ${import_picocolors28.default.bold((0, import_path8.join)(layout.appBase, "api", "ablo-session", "route.ts"))}`
286607
286973
  ] : [],
286608
- `Run ${import_picocolors27.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
286974
+ `Run ${import_picocolors28.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
286609
286975
  ...agent ? [
286610
- `Run ${import_picocolors27.default.bold(`npx tsx ${abloDir}/agent.ts`)} \u2014 an AI teammate edits the same tasks`,
286611
- `Run ${import_picocolors27.default.bold("npx ablo logs")} to watch human + agent commits stream by`
286976
+ `Run ${import_picocolors28.default.bold(`npx tsx ${abloDir}/agent.ts`)} \u2014 an AI teammate edits the same tasks`,
286977
+ `Run ${import_picocolors28.default.bold("npx ablo logs")} to watch human + agent commits stream by`
286612
286978
  ] : []
286613
286979
  ];
286614
286980
  Me(steps.map((s, i) => `${i + 1}. ${s}`).join("\n"), "Next steps");
286615
286981
  const existingKey = resolveManagementKey();
286616
286982
  if (existingKey) {
286617
286983
  await ensureInitProject(opts);
286618
- Se(`Already authorized ${import_picocolors27.default.dim(`(${existingKey.slice(0, 11)}\u2026)`)}. Run ${import_picocolors27.default.bold("npx ablo dev")} next. ${import_picocolors27.default.dim("Docs:")} https://abloatai.com/docs`);
286984
+ Se(`Already authorized ${import_picocolors28.default.dim(`(${existingKey.slice(0, 11)}\u2026)`)}. Run ${import_picocolors28.default.bold("npx ablo dev")} next. ${import_picocolors28.default.dim("Docs:")} https://abloatai.com/docs`);
286619
286985
  return;
286620
286986
  }
286621
286987
  if (interactive && opts.login) {
286622
286988
  const loginNow = await ye({ message: "Log in now? (opens your browser)", initialValue: true });
286623
286989
  if (!pD(loginNow) && loginNow) {
286624
- Se(`${import_picocolors27.default.dim("Docs:")} https://abloatai.com/docs`);
286990
+ Se(`${import_picocolors28.default.dim("Docs:")} https://abloatai.com/docs`);
286625
286991
  await login();
286626
286992
  await ensureInitProject(opts);
286627
286993
  return;
286628
286994
  }
286629
286995
  }
286630
- Se(`Run ${import_picocolors27.default.bold("npx ablo login")} when ready. ${import_picocolors27.default.dim("Docs:")} https://abloatai.com/docs`);
286996
+ Se(`Run ${import_picocolors28.default.bold("npx ablo login")} when ready. ${import_picocolors28.default.dim("Docs:")} https://abloatai.com/docs`);
286631
286997
  }
286632
286998
  function generateSchema() {
286633
286999
  return `import { defineSchema, model, relation, z } from '@abloatai/ablo/schema';
@@ -286702,7 +287068,7 @@ function generateEnv(storage, opts = {}) {
286702
287068
  const { includeApiKey = true } = opts;
286703
287069
  const databaseBlock = storage === "replication" ? "# Used by `npx ablo connect` to set up + register logical replication \u2014 the\n# DIRECT (un-pooled) endpoint. Ablo TAILS your WAL from here; it never writes.\n# The client never sees it; the browser never sees it. Your DB stays yours.\nDATABASE_URL=postgres://user:password@host:5432/db\n" : "# Used by ablo/data-source.ts (your DB endpoint) + `ablo migrate` \u2014 NOT the client.\n# Ablo never sees it; the browser never sees it. Your DB stays in your app.\nDATABASE_URL=postgres://user:password@host:5432/db\n";
286704
287070
  const webhookBlock = storage === "endpoint" ? "# Signing secret for the webhook receiver (app/api/ablo/webhooks/route.ts).\n# Ablo mints this when you register the endpoint's URL (POST /v1/webhook_endpoints\n# or the dashboard) and returns it once \u2014 paste it here.\nABLO_WEBHOOK_SECRET=whsec_your_endpoint_secret_here\n" : "";
286705
- const apiKeyBlock = includeApiKey ? "# Ablo: a sk_test_ key for local development (`npx ablo dev` wires this for you)\nABLO_API_KEY=sk_test_your_key_here\n" : "";
287071
+ const apiKeyBlock = includeApiKey ? "# Ablo: a branch-bound sk_ key (`npx ablo dev` wires a development branch for you)\nABLO_API_KEY=sk_your_key_here\n" : "";
286706
287072
  return `${apiKeyBlock}${webhookBlock}${databaseBlock}`;
286707
287073
  }
286708
287074
  function generateDataSource(orm) {
@@ -286778,8 +287144,8 @@ var WEBHOOK_DOC = `/**
286778
287144
  * signature, then write each change into YOUR database. The other half \u2014 your app
286779
287145
  * MAKING changes + live sync \u2014 is the Ablo client in \`ablo/index.ts\`.
286780
287146
  *
286781
- * Just like Stripe: you call Ablo to make changes (client) and Ablo calls you to
286782
- * persist them (this route). Reliability is built in \u2014 Ablo retries on any
287147
+ * Your app calls Ablo to make changes, and Ablo calls this route to persist
287148
+ * them. Reliability is built in \u2014 Ablo retries on any
286783
287149
  * non-2xx, and \`event.syncId\` is a monotonic log position, so apply in order and
286784
287150
  * dedupe (skip a \`syncId\` you've already stored).
286785
287151
  */`;