@abloatai/cli 0.41.0 → 0.42.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 +650 -323
  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({
@@ -3361,6 +3366,11 @@ function clearCredential() {
3361
3366
  function resolveApiKey(modeOverride) {
3362
3367
  return resolveKey({ purpose: "data", mode: modeOverride }).key;
3363
3368
  }
3369
+ function ambientEnvKeyNote(cwd) {
3370
+ const ambient = readProjectApiKey(cwd);
3371
+ if (!ambient || ambient.source === "env") return null;
3372
+ 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 the plane. Export the variable (or prefix the command with it) to use that key.`;
3373
+ }
3364
3374
  function resolveManagementKey() {
3365
3375
  if (process.env.ABLO_MANAGEMENT_KEY) return process.env.ABLO_MANAGEMENT_KEY;
3366
3376
  const cfg = readConfig();
@@ -3773,7 +3783,8 @@ async function confirmFromServer(opts) {
3773
3783
  branchId: identity.branchId ?? null,
3774
3784
  branchRoot: identity.branchRoot
3775
3785
  };
3776
- } catch {
3786
+ } catch (error) {
3787
+ if (opts.strict) throw error;
3777
3788
  return null;
3778
3789
  }
3779
3790
  }
@@ -5600,7 +5611,8 @@ function rotateWithoutConnection(input) {
5600
5611
  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
5612
  }
5602
5613
  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.";
5614
+ if (input.existingRoles.length > 0) return null;
5615
+ 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
5616
  }
5605
5617
  async function locateExistingConnection(input) {
5606
5618
  const result = await tryControlPlane({
@@ -5678,7 +5690,7 @@ async function executePlan(sql, steps, rebuildPlaintext) {
5678
5690
  async function runConnectApply(args) {
5679
5691
  const rotating = args.rotate;
5680
5692
  const verb = rotating ? "connect rotate" : "connect apply";
5681
- const adminUrl = args.url ?? readProjectAdminDatabaseUrl();
5693
+ let adminUrl = args.url ?? readProjectAdminDatabaseUrl();
5682
5694
  if (!adminUrl) {
5683
5695
  throw new import_errors9.AbloValidationError(
5684
5696
  "No admin connection string. Pass --url <admin-conn> (or set DATABASE_URL) and re-run.",
@@ -5695,12 +5707,17 @@ async function runConnectApply(args) {
5695
5707
  const apiKey = resolveApiKey();
5696
5708
  if (!apiKey) {
5697
5709
  const loggedIn = resolveManagementKey() !== void 0;
5710
+ const ambient = ambientEnvKeyNote();
5698
5711
  throw new import_errors9.AbloAuthenticationError(
5699
5712
  loggedIn ? `You are logged in, but this project has no ${getMode()} data key.
5700
5713
 
5701
5714
  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.
5702
5715
 
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.",
5716
+ 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).${ambient ? `
5717
+
5718
+ ${ambient}` : ""}` : `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 ? `
5719
+
5720
+ ${ambient}` : ""}`,
5704
5721
  { code: "cli_api_key_missing" }
5705
5722
  );
5706
5723
  }
@@ -5715,23 +5732,39 @@ Mint a sandbox key with \`npx ablo dev\`, or set ABLO_API_KEY to the key of the
5715
5732
  );
5716
5733
  const pooledAdmin = detectPooler(adminUrl);
5717
5734
  if (pooledAdmin?.confidence === "host") {
5718
- console.error(
5719
- ` ${import_picocolors11.default.yellow("!")} ${import_picocolors11.default.bold(target)} is a connection pooler, not the database.
5735
+ if (pooledAdmin.direct) {
5736
+ adminUrl = pooledAdmin.direct;
5737
+ const pooledLabel = target;
5738
+ try {
5739
+ const parsed = new URL(adminUrl);
5740
+ target = `${parsed.host}${parsed.pathname === "/" ? "" : parsed.pathname}`;
5741
+ } catch {
5742
+ }
5743
+ console.log(
5744
+ ` ${import_picocolors11.default.yellow("!")} ${import_picocolors11.default.bold(pooledLabel)} is a connection pooler, so this run uses the direct host:
5745
+ ${import_picocolors11.default.bold(target)}
5746
+ ` + import_picocolors11.default.dim(
5747
+ ` A pooler terminates the session, so replication cannot run over it. Your app
5748
+ keeps the pooled URL; the setup needs the database itself.
5720
5749
  `
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.
5750
+ )
5751
+ );
5752
+ } else {
5753
+ console.error(
5754
+ ` ${import_picocolors11.default.yellow("!")} ${import_picocolors11.default.bold(target)} is a connection pooler, not the database.
5726
5755
  `
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.
5756
+ );
5757
+ console.error(
5758
+ import_picocolors11.default.dim(
5759
+ ` A pooler terminates the session, so replication cannot run over it. Setting up
5760
+ through it creates roles that Ablo then cannot use to stream.
5732
5761
  `
5733
- );
5734
- process.exit(1);
5762
+ )
5763
+ );
5764
+ console.error(` Re-run against the direct database host, not the pooled one.
5765
+ `);
5766
+ process.exit(1);
5767
+ }
5735
5768
  }
5736
5769
  if (pooledAdmin?.confidence === "port") {
5737
5770
  console.log(
@@ -5771,31 +5804,31 @@ Mint a sandbox key with \`npx ablo dev\`, or set ABLO_API_KEY to the key of the
5771
5804
  `);
5772
5805
  console.error(
5773
5806
  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
- `
5807
+ `) + ` To move it here, disconnect it there first with ${import_picocolors11.default.cyan("ablo connect deregister")}
5808
+ ` + 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
5809
  );
5777
5810
  process.exit(1);
5778
5811
  }
5812
+ let rotatePlane = null;
5779
5813
  if (rotating) {
5780
5814
  const state = await fetchDataSourceState(apiBaseUrl(), apiKey).catch(
5781
5815
  () => ({ kind: "unknown", detail: "unreachable" })
5782
5816
  );
5783
- const refusal = rotateWithoutConnection({
5784
- rotating,
5817
+ rotatePlane = {
5785
5818
  planeHasConnection: state.kind === "connected",
5786
5819
  known: state.kind !== "unknown",
5787
5820
  // 401/403 is Ablo answering and declining the key, not a network failure.
5788
5821
  keyRejected: state.kind === "unknown" && /HTTP 40[13]/.test(state.detail)
5789
- });
5790
- if (refusal) {
5791
- console.error(` ${import_picocolors11.default.yellow("!")} ${refusal}
5822
+ };
5823
+ if (rotatePlane.keyRejected) {
5824
+ const refusal = rotateWithoutConnection({ rotating, ...rotatePlane, existingRoles: [] });
5825
+ if (refusal) {
5826
+ console.error(` ${import_picocolors11.default.yellow("!")} ${refusal}
5792
5827
  `);
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);
5828
+ console.error(import_picocolors11.default.dim(` Your database is untouched.
5829
+ `));
5830
+ process.exit(1);
5831
+ }
5799
5832
  }
5800
5833
  }
5801
5834
  const admin = src_default(adminUrl, {
@@ -5855,9 +5888,21 @@ Mint a sandbox key with \`npx ablo dev\`, or set ABLO_API_KEY to the key of the
5855
5888
  const pubReconcile = reconcilePublicationPlan(existingPublication, tables);
5856
5889
  const role = args.role && args.role.length > 0 ? args.role : import_footprint.ABLO_REPLICATION_ROLE;
5857
5890
  const writeRole = args.writeRole && args.writeRole.length > 0 ? args.writeRole : import_footprint.ABLO_WRITE_ROLE;
5891
+ const existingRoles = await presentRoles(admin, [role, writeRole]).catch(() => []);
5892
+ if (rotatePlane) {
5893
+ const refusal = rotateWithoutConnection({ rotating, ...rotatePlane, existingRoles });
5894
+ if (refusal) {
5895
+ await admin.end({ timeout: 2 });
5896
+ console.error(` ${import_picocolors11.default.yellow("!")} ${refusal}
5897
+ `);
5898
+ console.error(import_picocolors11.default.dim(` Your database is untouched.
5899
+ `));
5900
+ process.exit(1);
5901
+ }
5902
+ }
5858
5903
  const blocker = reapplyBlocker({
5859
5904
  rotating,
5860
- existingRoles: await presentRoles(admin, [role, writeRole]).catch(() => [])
5905
+ existingRoles
5861
5906
  });
5862
5907
  if (blocker) {
5863
5908
  await admin.end({ timeout: 2 });
@@ -6080,6 +6125,7 @@ function parseConnectArgs(argv) {
6080
6125
  let yes = false;
6081
6126
  let showSql = false;
6082
6127
  let scan = false;
6128
+ let locate = false;
6083
6129
  let manual = false;
6084
6130
  let tables = [];
6085
6131
  let role = import_footprint.ABLO_REPLICATION_ROLE;
@@ -6104,9 +6150,12 @@ function parseConnectArgs(argv) {
6104
6150
  case "scan":
6105
6151
  scan = true;
6106
6152
  break;
6153
+ case "locate":
6154
+ locate = true;
6155
+ break;
6107
6156
  default:
6108
6157
  throw new import_errors10.AbloValidationError(
6109
- `unknown connect subcommand: ${lead} (expected register, deregister, check, apply, rotate, scan)`,
6158
+ `unknown connect subcommand: ${lead} (expected register, deregister, check, apply, rotate, scan, locate)`,
6110
6159
  { code: "cli_invalid_arguments" }
6111
6160
  );
6112
6161
  }
@@ -6168,6 +6217,7 @@ function parseConnectArgs(argv) {
6168
6217
  yes,
6169
6218
  showSql,
6170
6219
  scan,
6220
+ locate,
6171
6221
  tables,
6172
6222
  role,
6173
6223
  writeRole,
@@ -6465,8 +6515,11 @@ async function runCheck() {
6465
6515
  );
6466
6516
  const apiKey = resolveApiKey();
6467
6517
  if (!apiKey) {
6518
+ const ambient = ambientEnvKeyNote();
6468
6519
  throw new import_errors10.AbloAuthenticationError(
6469
- "No API key found. Run `ablo login` (or set ABLO_API_KEY), then re-run `ablo connect check`.",
6520
+ `No API key found. Run \`ablo login\` (or set ABLO_API_KEY), then re-run \`ablo connect check\`.${ambient ? `
6521
+
6522
+ ${ambient}` : ""}`,
6470
6523
  { code: "cli_api_key_missing" }
6471
6524
  );
6472
6525
  }
@@ -6531,8 +6584,11 @@ async function runRegister(args) {
6531
6584
  const writeDbUrl = requireScopedUrl("write", "register");
6532
6585
  const apiKey = resolveApiKey();
6533
6586
  if (!apiKey) {
6587
+ const ambient = ambientEnvKeyNote();
6534
6588
  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.",
6589
+ `Not logged in. Run \`ablo login\` (or set ABLO_API_KEY) so Ablo knows which project to register this database for.${ambient ? `
6590
+
6591
+ ${ambient}` : ""}`,
6536
6592
  { code: "cli_api_key_missing" }
6537
6593
  );
6538
6594
  }
@@ -6633,6 +6689,62 @@ async function runScan() {
6633
6689
  }
6634
6690
  process.exit(retired.length > 0 ? 1 : 0);
6635
6691
  }
6692
+ async function runLocate(args) {
6693
+ console.log(
6694
+ `
6695
+ ${brand("ablo")} ${import_picocolors12.default.dim("connect locate")} ${import_picocolors12.default.dim("which plane holds this database")}
6696
+ `
6697
+ );
6698
+ const url = args.url ?? readProjectAdminDatabaseUrl();
6699
+ if (!url) {
6700
+ throw new import_errors10.AbloValidationError(
6701
+ "Locating needs a connection string to identify the database. Pass --url <conn> (or set DATABASE_URL) and re-run.",
6702
+ { code: "cli_database_url_missing" }
6703
+ );
6704
+ }
6705
+ const apiKey = resolveApiKey();
6706
+ if (!apiKey) {
6707
+ const ambient = ambientEnvKeyNote();
6708
+ throw new import_errors10.AbloAuthenticationError(
6709
+ `No API key found. Run \`ablo login\` (or set ABLO_API_KEY), then re-run \`ablo connect locate\`.${ambient ? `
6710
+
6711
+ ${ambient}` : ""}`,
6712
+ { code: "cli_api_key_missing" }
6713
+ );
6714
+ }
6715
+ let label = "this database";
6716
+ try {
6717
+ const parsed = new URL(url);
6718
+ label = `${parsed.host}${parsed.pathname === "/" ? "" : parsed.pathname}`;
6719
+ } catch {
6720
+ }
6721
+ const answer = await requestControlPlane({
6722
+ path: "/v1/datasources/locate",
6723
+ method: "POST",
6724
+ apiKey,
6725
+ body: { connectionString: url },
6726
+ responseSchema: import_wire7.datasourceLocationResponseSchema
6727
+ });
6728
+ if (!answer.held) {
6729
+ console.log(
6730
+ ` ${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.
6731
+ `
6732
+ );
6733
+ return;
6734
+ }
6735
+ console.log(
6736
+ ` ${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)}.
6737
+ `
6738
+ );
6739
+ console.log(
6740
+ import_picocolors12.default.dim(
6741
+ ` Ablo streams a database from one plane at a time. To move it, disconnect it there
6742
+ first \u2014 run `
6743
+ ) + import_picocolors12.default.cyan("ablo connect deregister") + import_picocolors12.default.dim(` with a key for that plane, then connect here.
6744
+ `) + import_picocolors12.default.dim(` Confirm a candidate key first with `) + import_picocolors12.default.bold("ablo whoami --key-env <NAME>") + import_picocolors12.default.dim(`.
6745
+ `) + 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"
6746
+ );
6747
+ }
6636
6748
  async function connect(argv) {
6637
6749
  if (argv[0] === "deregister") {
6638
6750
  const { disconnect: disconnect2 } = await Promise.resolve().then(() => (init_disconnect(), disconnect_exports));
@@ -6657,6 +6769,10 @@ async function connect(argv) {
6657
6769
  await runScan();
6658
6770
  return;
6659
6771
  }
6772
+ if (args.locate) {
6773
+ await runLocate(args);
6774
+ return;
6775
+ }
6660
6776
  const credentialReachable = (args.url ?? readProjectAdminDatabaseUrl()) != null;
6661
6777
  const canConfirm = process.stdout.isTTY || args.yes;
6662
6778
  if (!args.manual && credentialReachable && canConfirm) {
@@ -6666,7 +6782,7 @@ async function connect(argv) {
6666
6782
  }
6667
6783
  printConnectRecipe(args);
6668
6784
  }
6669
- var import_errors10, import_picocolors12, import_footprint2, FOOTPRINT_LOOKUP, CONNECT_USAGE;
6785
+ var import_errors10, import_picocolors12, import_footprint2, import_wire7, FOOTPRINT_LOOKUP, CONNECT_USAGE;
6670
6786
  var init_connect = __esm({
6671
6787
  "src/connect.ts"() {
6672
6788
  "use strict";
@@ -6678,6 +6794,7 @@ var init_connect = __esm({
6678
6794
  init_dbRole();
6679
6795
  init_config();
6680
6796
  init_controlPlane();
6797
+ import_wire7 = require("@abloatai/transaction/wire");
6681
6798
  init_theme();
6682
6799
  init_remoteValidation();
6683
6800
  init_connectSetup();
@@ -6701,6 +6818,7 @@ var init_connect = __esm({
6701
6818
  npx ablo connect check Confirm the connected database is ready, from Ablo's side (needs only ABLO_API_KEY)
6702
6819
  npx ablo connect rotate New passwords for both logins, then re-register
6703
6820
  npx ablo connect scan List anything Ablo ever set up in your database (read-only, never drops)
6821
+ npx ablo connect locate See which plane holds this database (read-only; nothing is changed)
6704
6822
 
6705
6823
  Running it: bare \`ablo connect\` sets everything up for you \u2014 creating the two
6706
6824
  scoped logins, sharing your tables, and registering \u2014 whenever it finds a
@@ -282702,7 +282820,7 @@ Node text: ${this.#forgottenText}`;
282702
282820
  // src/index.ts
282703
282821
  init_cjs_shims();
282704
282822
  init_dist2();
282705
- var import_picocolors27 = __toESM(require_picocolors(), 1);
282823
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
282706
282824
  var import_fs13 = require("fs");
282707
282825
  var import_path8 = require("path");
282708
282826
  var import_child_process3 = require("child_process");
@@ -283736,6 +283854,223 @@ async function runBranchDev(argv, dependencies = {}) {
283736
283854
  });
283737
283855
  }
283738
283856
 
283857
+ // src/whoami.ts
283858
+ init_cjs_shims();
283859
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
283860
+ var import_errors13 = require("@abloatai/transaction/errors");
283861
+ init_config();
283862
+ init_controlPlane();
283863
+
283864
+ // src/credentialCapability.ts
283865
+ init_cjs_shims();
283866
+ var import_credentialPolicy3 = require("@abloatai/transaction/auth/credentialPolicy");
283867
+ init_config();
283868
+ function secretCounterpart(key) {
283869
+ return modeFromKey(key) === "production" ? "sk_live_" : "sk_test_";
283870
+ }
283871
+ function credentialCapability(key) {
283872
+ const kind = key ? (0, import_credentialPolicy3.classifyCredentialKind)(key) : null;
283873
+ if (!key || kind === null) return { kind, label: "", note: null };
283874
+ const secret = `${secretCounterpart(key)}\u2026`;
283875
+ switch (kind) {
283876
+ case "secret":
283877
+ return { kind, label: "", note: null };
283878
+ case "restricted":
283879
+ return {
283880
+ kind,
283881
+ label: "scoped",
283882
+ 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.`
283883
+ };
283884
+ case "publishable":
283885
+ return {
283886
+ kind,
283887
+ label: "read-only",
283888
+ 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.`
283889
+ };
283890
+ case "ephemeral":
283891
+ return {
283892
+ kind,
283893
+ label: "session key",
283894
+ note: `This is a short-lived credential minted for one signed-in person, and it expires. Pushing a schema needs a secret ${secret} key.`
283895
+ };
283896
+ }
283897
+ }
283898
+
283899
+ // src/whoami.ts
283900
+ init_dbRole();
283901
+ init_theme();
283902
+ init_target();
283903
+ var WHOAMI_USAGE = ` ablo whoami \u2014 show the plane a credential acts on
283904
+
283905
+ Usage
283906
+ npx ablo whoami
283907
+ npx ablo whoami --key-env <NAME>
283908
+ npx ablo whoami --key <VALUE>
283909
+ npx ablo whoami --json
283910
+
283911
+ Credential choice
283912
+ With no flag, uses ABLO_API_KEY, then the active project's stored data key,
283913
+ then the stored login. This command does not load .env files implicitly.
283914
+
283915
+ --key-env reads a named variable from the process, .env.local, or .env
283916
+ without putting its value in shell history or the process list. Prefer it
283917
+ for comparing several keys.
283918
+ --key accepts a value directly for one-off use.
283919
+
283920
+ Output never prints the full credential. Identity is confirmed by the server;
283921
+ an invalid key, unreachable server, or unsupported server fails non-zero.`;
283922
+ function parseWhoamiArgs(argv) {
283923
+ let json = false;
283924
+ let key;
283925
+ let keyEnv;
283926
+ for (let i = 0; i < argv.length; i++) {
283927
+ const arg = argv[i];
283928
+ switch (arg) {
283929
+ case "--json":
283930
+ json = true;
283931
+ break;
283932
+ case "--key": {
283933
+ const value = argv[++i];
283934
+ if (!value || value.startsWith("--")) {
283935
+ throw new import_errors13.AbloValidationError("`--key` needs a credential value.", {
283936
+ code: "cli_invalid_arguments"
283937
+ });
283938
+ }
283939
+ key = value;
283940
+ break;
283941
+ }
283942
+ case "--key-env": {
283943
+ const value = argv[++i];
283944
+ if (!value || value.startsWith("--")) {
283945
+ throw new import_errors13.AbloValidationError("`--key-env` needs an environment variable name.", {
283946
+ code: "cli_invalid_arguments"
283947
+ });
283948
+ }
283949
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
283950
+ throw new import_errors13.AbloValidationError(
283951
+ `\`${value}\` is not a valid environment variable name.`,
283952
+ { code: "cli_invalid_arguments" }
283953
+ );
283954
+ }
283955
+ keyEnv = value;
283956
+ break;
283957
+ }
283958
+ default:
283959
+ throw new import_errors13.AbloValidationError(`unknown whoami flag: ${arg}`, {
283960
+ code: "cli_invalid_arguments"
283961
+ });
283962
+ }
283963
+ }
283964
+ if (key && keyEnv) {
283965
+ throw new import_errors13.AbloValidationError("Choose one credential source: `--key` or `--key-env`.", {
283966
+ code: "cli_invalid_arguments"
283967
+ });
283968
+ }
283969
+ return { json, ...key ? { key } : {}, ...keyEnv ? { keyEnv } : {} };
283970
+ }
283971
+ function selectWhoamiCredential(args, cwd = process.cwd()) {
283972
+ if (args.key) return { key: args.key, source: "--key", targetSource: "env" };
283973
+ if (args.keyEnv) {
283974
+ const found = readProjectEnvVariable(args.keyEnv, cwd);
283975
+ if (!found) {
283976
+ throw new import_errors13.AbloAuthenticationError(
283977
+ `${args.keyEnv} is not set in the process environment, .env.local, or .env.`,
283978
+ { code: "cli_api_key_missing" }
283979
+ );
283980
+ }
283981
+ return {
283982
+ key: found.value,
283983
+ source: `${found.source}:${args.keyEnv}`,
283984
+ targetSource: "env"
283985
+ };
283986
+ }
283987
+ const dataKey = resolveApiKey();
283988
+ if (dataKey) {
283989
+ return {
283990
+ key: dataKey,
283991
+ source: process.env.ABLO_API_KEY ? "env:ABLO_API_KEY" : "stored data key",
283992
+ targetSource: process.env.ABLO_API_KEY ? "env" : "stored"
283993
+ };
283994
+ }
283995
+ const managementKey = resolveManagementKey();
283996
+ if (managementKey) {
283997
+ return {
283998
+ key: managementKey,
283999
+ source: process.env.ABLO_MANAGEMENT_KEY ? "env:ABLO_MANAGEMENT_KEY" : "stored login",
284000
+ targetSource: process.env.ABLO_MANAGEMENT_KEY ? "env" : "stored"
284001
+ };
284002
+ }
284003
+ const ambient = ambientEnvKeyNote();
284004
+ throw new import_errors13.AbloAuthenticationError(
284005
+ `No credential found. Run \`ablo login\`, set ABLO_API_KEY, or pass \`--key-env <NAME>\`.${ambient ? `
284006
+
284007
+ ${ambient}` : ""}`,
284008
+ { code: "cli_api_key_missing" }
284009
+ );
284010
+ }
284011
+ async function whoami(argv) {
284012
+ const args = parseWhoamiArgs(argv);
284013
+ const selected = selectWhoamiCredential(args);
284014
+ const target = await resolveTarget({
284015
+ url: apiBaseUrl(),
284016
+ apiKey: selected.key,
284017
+ keySource: selected.targetSource,
284018
+ strict: true
284019
+ });
284020
+ const confirmed = target.confirmed;
284021
+ if (!confirmed) {
284022
+ throw new import_errors13.AbloAuthenticationError(
284023
+ "The server did not confirm an identity for this credential.",
284024
+ { code: "identity_resolve_failed" }
284025
+ );
284026
+ }
284027
+ const project = confirmed.project;
284028
+ const projectLabel = project ? project.isDefault ? "default" : project.slug : "default";
284029
+ const actsOn = confirmed.branchId ? confirmed.branchRoot ? "production root" : `branch ${confirmed.branchId}` : confirmed.environment ?? "unknown";
284030
+ const capability = credentialCapability(selected.key);
284031
+ if (args.json) {
284032
+ console.log(
284033
+ JSON.stringify(
284034
+ {
284035
+ authenticated: true,
284036
+ key: {
284037
+ prefix: `${selected.key.slice(0, 12)}\u2026`,
284038
+ source: selected.source,
284039
+ kind: capability.kind
284040
+ },
284041
+ organizationId: confirmed.organizationId,
284042
+ project: project ? {
284043
+ id: project.id,
284044
+ slug: projectLabel,
284045
+ name: project.name,
284046
+ default: project.isDefault
284047
+ } : null,
284048
+ environment: confirmed.environment,
284049
+ branchId: confirmed.branchId ?? null,
284050
+ branchRoot: confirmed.branchRoot ?? false
284051
+ },
284052
+ null,
284053
+ 2
284054
+ )
284055
+ );
284056
+ return;
284057
+ }
284058
+ console.log(`
284059
+ ${brand("ablo")} ${import_picocolors16.default.dim("whoami")}
284060
+ `);
284061
+ console.log(
284062
+ ` ${import_picocolors16.default.dim("key")} ${selected.key.slice(0, 12)}\u2026 ${import_picocolors16.default.dim(`(${selected.source} \xB7 ${capability.label})`)}`
284063
+ );
284064
+ console.log(` ${import_picocolors16.default.dim("org")} ${import_picocolors16.default.dim(confirmed.organizationId)}`);
284065
+ console.log(
284066
+ ` ${import_picocolors16.default.dim("project")} ${import_picocolors16.default.bold(projectLabel)}${project ? ` ${import_picocolors16.default.dim(`(${project.id})`)}` : ""}`
284067
+ );
284068
+ console.log(` ${import_picocolors16.default.dim("acts on")} ${import_picocolors16.default.bold(actsOn)}`);
284069
+ console.log(`
284070
+ ${import_picocolors16.default.green("\u2713")} ${import_picocolors16.default.dim("credential accepted; target confirmed by the server")}
284071
+ `);
284072
+ }
284073
+
283739
284074
  // src/commands.ts
283740
284075
  var CORE_GROUPS = ["Start", "Every day", "More"];
283741
284076
  var FULL_GROUPS = [
@@ -283786,6 +284121,7 @@ var COMMANDS = [
283786
284121
  { run: "connect apply", does: "Run that setup for you, from a one-time admin URL" },
283787
284122
  { run: "connect check", does: "Confirm your database is ready to share changes with Ablo" },
283788
284123
  { run: "connect scan", does: "List anything Ablo ever set up in your database (read-only)" },
284124
+ { run: "connect locate", does: "See which plane holds a database before connecting it" },
283789
284125
  { run: "connect deregister", does: "Disconnect this project's database \u2014 Ablo stops reading and writing it" }
283790
284126
  ]
283791
284127
  }
@@ -283875,6 +284211,18 @@ var COMMANDS = [
283875
284211
  ]
283876
284212
  }
283877
284213
  },
284214
+ {
284215
+ name: "whoami",
284216
+ usage: WHOAMI_USAGE,
284217
+ full: {
284218
+ group: "See what's happening",
284219
+ rows: [
284220
+ { run: "whoami", does: "Show the server-confirmed plane the active credential acts on" },
284221
+ { run: "whoami --key-env <NAME>", does: "Inspect another key without exposing it in argv" },
284222
+ { run: "whoami --json", does: "Same, machine-readable" }
284223
+ ]
284224
+ }
284225
+ },
283878
284226
  {
283879
284227
  name: "status",
283880
284228
  core: { group: "Every day", does: "See what this key acts on, your pushed schema, and whether writes will work" },
@@ -284003,15 +284351,15 @@ function fullRows(group) {
284003
284351
  }
284004
284352
 
284005
284353
  // src/index.ts
284006
- var import_errors20 = require("@abloatai/transaction/errors");
284354
+ var import_errors21 = require("@abloatai/transaction/errors");
284007
284355
  init_push();
284008
284356
 
284009
284357
  // src/generate.ts
284010
284358
  init_cjs_shims();
284011
- var import_errors13 = require("@abloatai/transaction/errors");
284359
+ var import_errors14 = require("@abloatai/transaction/errors");
284012
284360
  var import_fs8 = require("fs");
284013
284361
  var import_path6 = require("path");
284014
- var import_picocolors16 = __toESM(require_picocolors(), 1);
284362
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
284015
284363
  var import_schema7 = require("@abloatai/transaction/schema");
284016
284364
  init_push();
284017
284365
  var DEFAULT_SCHEMA_PATH4 = "ablo/schema.ts";
@@ -284034,7 +284382,7 @@ function parseGenerateArgs(argv) {
284034
284382
  out = argv[++i] ?? out;
284035
284383
  break;
284036
284384
  default:
284037
- throw new import_errors13.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
284385
+ throw new import_errors14.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
284038
284386
  }
284039
284387
  }
284040
284388
  return { schemaPath, exportName, out };
@@ -284044,7 +284392,7 @@ async function generate(argv) {
284044
284392
  try {
284045
284393
  args = parseGenerateArgs(argv);
284046
284394
  } catch (err) {
284047
- console.error(import_picocolors16.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284395
+ console.error(import_picocolors17.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284048
284396
  process.exit(1);
284049
284397
  }
284050
284398
  let source;
@@ -284053,22 +284401,22 @@ async function generate(argv) {
284053
284401
  const schemaJson = JSON.parse((0, import_schema7.serializeSchema)(schema));
284054
284402
  source = (0, import_schema7.generateTypes)(schemaJson);
284055
284403
  } catch (err) {
284056
- console.error(import_picocolors16.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284404
+ console.error(import_picocolors17.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284057
284405
  process.exit(1);
284058
284406
  }
284059
284407
  const abs = (0, import_path6.resolve)(process.cwd(), args.out);
284060
284408
  (0, import_fs8.mkdirSync)((0, import_path6.dirname)(abs), { recursive: true });
284061
284409
  (0, import_fs8.writeFileSync)(abs, source);
284062
- console.log(` ${import_picocolors16.default.green("\u2713")} Generated types \u2192 ${import_picocolors16.default.bold(args.out)}`);
284410
+ console.log(` ${import_picocolors17.default.green("\u2713")} Generated types \u2192 ${import_picocolors17.default.bold(args.out)}`);
284063
284411
  }
284064
284412
 
284065
284413
  // src/login.ts
284066
284414
  init_cjs_shims();
284067
284415
  var import_child_process2 = require("child_process");
284068
- var import_picocolors17 = __toESM(require_picocolors(), 1);
284416
+ var import_picocolors18 = __toESM(require_picocolors(), 1);
284069
284417
  init_dist2();
284070
- var import_errors14 = require("@abloatai/transaction/errors");
284071
- var import_wire7 = require("@abloatai/transaction/wire");
284418
+ var import_errors15 = require("@abloatai/transaction/errors");
284419
+ var import_wire8 = require("@abloatai/transaction/wire");
284072
284420
  init_config();
284073
284421
  init_theme();
284074
284422
  var CLIENT_ID = "ablo-cli";
@@ -284086,20 +284434,21 @@ function openBrowser(url) {
284086
284434
  } catch {
284087
284435
  }
284088
284436
  }
284089
- function parseProjectFlag(argv) {
284090
- const i = argv.indexOf("--project");
284437
+ function parseSlugFlag(argv, flag2) {
284438
+ const i = argv.indexOf(flag2);
284091
284439
  if (i >= 0) {
284092
284440
  const slug = argv[i + 1];
284093
284441
  if (slug && !slug.startsWith("-")) return slug;
284094
284442
  }
284095
- const eq = argv.find((a) => a.startsWith("--project="));
284096
- return eq ? eq.slice("--project=".length) || void 0 : void 0;
284443
+ const eq = argv.find((a) => a.startsWith(`${flag2}=`));
284444
+ return eq ? eq.slice(flag2.length + 1) || void 0 : void 0;
284097
284445
  }
284098
284446
  async function deviceLogin(argv, deps = {}) {
284099
284447
  const openUrl = deps.openUrl ?? openBrowser;
284100
284448
  Ie(`${brand("ablo")} login`);
284101
- const requested = parseProjectFlag(argv) ?? getActiveProject()?.slug;
284449
+ const requested = parseSlugFlag(argv, "--project") ?? getActiveProject()?.slug;
284102
284450
  const targetProject = requested === DEFAULT_PROFILE ? void 0 : requested;
284451
+ const targetOrg = parseSlugFlag(argv, "--org");
284103
284452
  const interactive = Boolean(process.stdout.isTTY && process.stdin.isTTY);
284104
284453
  let account = "login";
284105
284454
  if (interactive) {
@@ -284126,11 +284475,11 @@ async function deviceLogin(argv, deps = {}) {
284126
284475
  process.exit(1);
284127
284476
  }
284128
284477
  const code = await codeRes.json();
284129
- const approvePath = `/cli?user_code=${code.user_code}`;
284478
+ const approvePath = `/cli?user_code=${code.user_code}${targetOrg ? `&org=${encodeURIComponent(targetOrg)}` : ""}`;
284130
284479
  const url = account === "signup" ? `${DASHBOARD_URL}/signup?next=${encodeURIComponent(approvePath)}` : `${DASHBOARD_URL}${approvePath}`;
284131
- Me(`${import_picocolors17.default.bold(code.user_code)}
284480
+ Me(`${import_picocolors18.default.bold(code.user_code)}
284132
284481
 
284133
- ${import_picocolors17.default.dim(url)}`, "Approve in your browser");
284482
+ ${import_picocolors18.default.dim(url)}`, "Approve in your browser");
284134
284483
  openUrl(url);
284135
284484
  const s = Y2();
284136
284485
  s.start("Waiting for approval\u2026");
@@ -284200,7 +284549,7 @@ ${import_picocolors17.default.dim(url)}`, "Approve in your browser");
284200
284549
  }
284201
284550
  if (!provRes.ok) {
284202
284551
  s.stop("Could not provision a key.");
284203
- const err = (0, import_errors14.translateHttpError)(
284552
+ const err = (0, import_errors15.translateHttpError)(
284204
284553
  provRes.status,
284205
284554
  await provRes.json().catch(() => null),
284206
284555
  provRes.headers.get("x-request-id") ?? void 0
@@ -284208,30 +284557,36 @@ ${import_picocolors17.default.dim(url)}`, "Approve in your browser");
284208
284557
  M2.error(err.message);
284209
284558
  if (err.code === "entity_not_found" && targetProject) {
284210
284559
  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}`)}.`
284560
+ `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
284561
  );
284213
284562
  } else {
284214
284563
  M2.error(
284215
- `The browser approval succeeded but the credential handoff failed. Try ${import_picocolors17.default.bold("npx ablo login")} again.`
284564
+ `The browser approval succeeded but the credential handoff failed. Try ${import_picocolors18.default.bold("npx ablo login")} again.`
284216
284565
  );
284217
284566
  }
284218
284567
  process.exit(1);
284219
284568
  }
284220
- const parsedProv = import_wire7.provisionKeyResponseSchema.safeParse(
284569
+ const parsedProv = import_wire8.provisionKeyResponseSchema.safeParse(
284221
284570
  await provRes.json().catch(() => null)
284222
284571
  );
284223
284572
  if (!parsedProv.success) {
284224
284573
  s.stop("Could not provision a key.");
284225
284574
  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")}.`);
284575
+ M2.error(`Try again, or upgrade with ${import_picocolors18.default.bold("npm i -g @abloatai/ablo")}.`);
284227
284576
  process.exit(1);
284228
284577
  }
284229
284578
  const prov = parsedProv.data;
284230
284579
  const entry = (k3) => ({
284231
284580
  apiKey: k3.apiKey,
284232
284581
  ...prov.organizationId ? { organizationId: prov.organizationId } : {},
284582
+ ...prov.organizationSlug ? { organizationSlug: prov.organizationSlug } : {},
284233
284583
  ...k3.expiresAt ? { expiresAt: k3.expiresAt } : {}
284234
284584
  });
284585
+ if (targetOrg && prov.organizationSlug && prov.organizationSlug !== targetOrg) {
284586
+ M2.warn(
284587
+ `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)}.`
284588
+ );
284589
+ }
284235
284590
  const profileName = prov.project?.slug ?? DEFAULT_PROFILE;
284236
284591
  const path = setProfileKeys(
284237
284592
  profileName,
@@ -284241,9 +284596,10 @@ ${import_picocolors17.default.dim(url)}`, "Approve in your browser");
284241
284596
  { mode: "sandbox", activeProject: prov.project ?? void 0 }
284242
284597
  );
284243
284598
  s.stop(`Saved project credential to ${path}`);
284244
- const where = prov.project ? ` ${import_picocolors17.default.dim(`(project ${prov.project.slug})`)}` : "";
284599
+ const orgLabel = prov.organizationSlug ? ` to ${import_picocolors18.default.bold(prov.organizationSlug)}` : "";
284600
+ const where = prov.project ? ` ${import_picocolors18.default.dim(`(project ${prov.project.slug})`)}` : "";
284245
284601
  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.`
284602
+ `${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
284603
  );
284248
284604
  }
284249
284605
  async function login(argv = [], deps = {}) {
@@ -284252,14 +284608,14 @@ async function login(argv = [], deps = {}) {
284252
284608
  function logout() {
284253
284609
  const removed = clearCredential();
284254
284610
  if (removed) {
284255
- console.log(` ${import_picocolors17.default.green("\u2713")} Logged out ${import_picocolors17.default.dim(`(credentials removed from ${configDir()})`)}`);
284611
+ console.log(` ${import_picocolors18.default.green("\u2713")} Logged out ${import_picocolors18.default.dim(`(credentials removed from ${configDir()})`)}`);
284256
284612
  } else {
284257
- console.log(` ${import_picocolors17.default.dim("\u25CB")} Not logged in \u2014 nothing to remove.`);
284613
+ console.log(` ${import_picocolors18.default.dim("\u25CB")} Not logged in \u2014 nothing to remove.`);
284258
284614
  }
284259
284615
  if (process.env.ABLO_MANAGEMENT_KEY) {
284260
284616
  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.`
284617
+ import_picocolors18.default.dim(
284618
+ ` Note: ${import_picocolors18.default.bold("ABLO_MANAGEMENT_KEY")} is still set in this shell and takes precedence.`
284263
284619
  )
284264
284620
  );
284265
284621
  }
@@ -284271,46 +284627,9 @@ init_projects();
284271
284627
 
284272
284628
  // src/status.ts
284273
284629
  init_cjs_shims();
284274
- var import_picocolors18 = __toESM(require_picocolors(), 1);
284630
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
284275
284631
  init_config();
284276
284632
  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
284633
  init_theme();
284315
284634
  init_controlPlane();
284316
284635
  var import_schema8 = require("@abloatai/transaction/coordination/schema");
@@ -284318,9 +284637,9 @@ init_readiness();
284318
284637
  function expiryLabel(iso) {
284319
284638
  const ms = Date.parse(iso) - Date.now();
284320
284639
  if (Number.isNaN(ms)) return "";
284321
- if (ms <= 0) return import_picocolors18.default.red("expired");
284640
+ if (ms <= 0) return import_picocolors19.default.red("expired");
284322
284641
  const days = Math.floor(ms / (24 * 60 * 60 * 1e3));
284323
- return import_picocolors18.default.dim(days > 0 ? `expires in ${days}d` : "expires <1d");
284642
+ return import_picocolors19.default.dim(days > 0 ? `expires in ${days}d` : "expires <1d");
284324
284643
  }
284325
284644
  async function ping(apiUrl3) {
284326
284645
  const ctrl = new AbortController();
@@ -284341,40 +284660,42 @@ function formatConflict(conflict) {
284341
284660
  const parts = import_schema8.participantKindSchema.options.flatMap((k3) => conflict[k3] ? [`${k3}:${conflict[k3]}`] : []);
284342
284661
  return parts.length ? `{${parts.join(",")}}` : "";
284343
284662
  }
284344
- function printTargetLines(target, localProject, storedOrganizationId) {
284663
+ function printTargetLines(target, localProject, storedOrganizationId, storedOrganizationSlug) {
284345
284664
  const confirmed = target?.confirmed ?? null;
284346
284665
  const org = confirmed?.organizationId ?? storedOrganizationId;
284347
284666
  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}`);
284667
+ const suffix = confirmed?.organizationId ? "" : ` ${import_picocolors19.default.yellow("(unconfirmed)")}`;
284668
+ const slug = org === storedOrganizationId ? storedOrganizationSlug : void 0;
284669
+ const label = slug ? `${import_picocolors19.default.bold(slug)} ${import_picocolors19.default.dim(`(${org})`)}` : import_picocolors19.default.dim(org);
284670
+ console.log(` ${import_picocolors19.default.dim("org")} ${label}${suffix}`);
284350
284671
  } else {
284351
284672
  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)")}`
284673
+ ` ${import_picocolors19.default.dim("org")} ${import_picocolors19.default.yellow("unknown")} ${import_picocolors19.default.dim("(the server did not confirm one for this key)")}`
284353
284674
  );
284354
284675
  }
284355
284676
  let projectLine;
284356
284677
  if (confirmed?.project) {
284357
284678
  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})`)}`;
284679
+ 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
284680
  } else if (confirmed) {
284360
- projectLine = `${import_picocolors18.default.bold("default")} ${import_picocolors18.default.dim("(org-default)")}`;
284681
+ projectLine = `${import_picocolors19.default.bold("default")} ${import_picocolors19.default.dim("(org-default)")}`;
284361
284682
  } else if (localProject) {
284362
- projectLine = `${import_picocolors18.default.bold(localProject.slug)} ${import_picocolors18.default.dim(`(${localProject.id})`)} ${import_picocolors18.default.yellow("(unconfirmed)")}`;
284683
+ projectLine = `${import_picocolors19.default.bold(localProject.slug)} ${import_picocolors19.default.dim(`(${localProject.id})`)} ${import_picocolors19.default.yellow("(unconfirmed)")}`;
284363
284684
  } else {
284364
- projectLine = `${import_picocolors18.default.bold("default")} ${target ? import_picocolors18.default.yellow("(unconfirmed)") : import_picocolors18.default.dim("(org-default)")}`;
284685
+ projectLine = `${import_picocolors19.default.bold("default")} ${target ? import_picocolors19.default.yellow("(unconfirmed)") : import_picocolors19.default.dim("(org-default)")}`;
284365
284686
  }
284366
- console.log(` ${import_picocolors18.default.dim("project")} ${projectLine}`);
284687
+ console.log(` ${import_picocolors19.default.dim("project")} ${projectLine}`);
284367
284688
  const branch = confirmed?.branchId ?? null;
284368
284689
  const env = confirmed?.environment ?? target?.keyEnv ?? null;
284369
284690
  if (branch) {
284370
284691
  const label = confirmed?.branchRoot ? "production root" : `branch ${branch}`;
284371
- console.log(` ${import_picocolors18.default.dim("acts on")} ${import_picocolors18.default.bold(label)}`);
284692
+ console.log(` ${import_picocolors19.default.dim("acts on")} ${import_picocolors19.default.bold(label)}`);
284372
284693
  } 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}`);
284694
+ const suffix = confirmed ? "" : ` ${import_picocolors19.default.yellow("(unconfirmed)")}`;
284695
+ console.log(` ${import_picocolors19.default.dim("acts on")} ${import_picocolors19.default.bold(env)}${suffix}`);
284375
284696
  }
284376
284697
  const divergence = describeMismatches(target?.mismatches ?? []);
284377
- if (divergence) console.log(` ${import_picocolors18.default.yellow(`\u26A0 ${divergence}`)}`);
284698
+ if (divergence) console.log(` ${import_picocolors19.default.yellow(`\u26A0 ${divergence}`)}`);
284378
284699
  }
284379
284700
  async function status(args = []) {
284380
284701
  const apiUrl3 = apiBaseUrl();
@@ -284463,23 +284784,28 @@ async function status(args = []) {
284463
284784
  return;
284464
284785
  }
284465
284786
  console.log(`
284466
- ${brand("ablo")} ${import_picocolors18.default.dim("status")}
284787
+ ${brand("ablo")} ${import_picocolors19.default.dim("status")}
284467
284788
  `);
284468
284789
  if (effective.key && effective.source && effective.source !== "stored") {
284469
284790
  const label = effective.source === "env" ? "ABLO_API_KEY env" : effective.source;
284470
284791
  console.log(
284471
- ` ${import_picocolors18.default.dim("key")} ${effective.key.slice(0, 12)}\u2026 ${import_picocolors18.default.dim(`(${label} \u2014 overrides stored)`)}`
284792
+ ` ${import_picocolors19.default.dim("key")} ${effective.key.slice(0, 12)}\u2026 ${import_picocolors19.default.dim(`(${label} \u2014 overrides stored)`)}`
284472
284793
  );
284473
284794
  } else if (!cfg) {
284474
- console.log(` ${import_picocolors18.default.yellow("!")} Not logged in \u2014 run ${import_picocolors18.default.bold("ablo login")}.`);
284795
+ console.log(` ${import_picocolors19.default.yellow("!")} Not logged in \u2014 run ${import_picocolors19.default.bold("ablo login")}.`);
284475
284796
  }
284476
284797
  const activeEntry = getKeyEntry(mode);
284477
284798
  const key = describeEffectiveKey(mode, process.env.ABLO_API_KEY, activeEntry);
284478
284799
  if (key.keyMismatch) {
284479
- console.log(` ${import_picocolors18.default.yellow(`! ${key.keyMismatch.message}`)}`);
284800
+ console.log(` ${import_picocolors19.default.yellow(`! ${key.keyMismatch.message}`)}`);
284480
284801
  }
284481
284802
  const activeProject = getActiveProject();
284482
- printTargetLines(target, activeProject, activeEntry?.organizationId);
284803
+ printTargetLines(
284804
+ target,
284805
+ activeProject,
284806
+ activeEntry?.organizationId,
284807
+ activeEntry?.organizationSlug
284808
+ );
284483
284809
  for (const { key: m2, label } of [
284484
284810
  { key: "sandbox", label: "management" },
284485
284811
  { key: "production", label: "observer" }
@@ -284490,68 +284816,68 @@ async function status(args = []) {
284490
284816
  credentialCapability(entry.apiKey).label,
284491
284817
  entry.expiresAt ? expiryLabel(entry.expiresAt) : ""
284492
284818
  ].filter(Boolean);
284493
- const trail = facts.length ? ` ${import_picocolors18.default.dim("\xB7")} ${facts.join(import_picocolors18.default.dim(" \xB7 "))}` : "";
284819
+ const trail = facts.length ? ` ${import_picocolors19.default.dim("\xB7")} ${facts.join(import_picocolors19.default.dim(" \xB7 "))}` : "";
284494
284820
  console.log(
284495
- ` ${import_picocolors18.default.dim("\u25CB")} ${label.padEnd(10)} ${import_picocolors18.default.dim(`${entry.apiKey.slice(0, 12)}\u2026`)}${trail}`
284821
+ ` ${import_picocolors19.default.dim("\u25CB")} ${label.padEnd(10)} ${import_picocolors19.default.dim(`${entry.apiKey.slice(0, 12)}\u2026`)}${trail}`
284496
284822
  );
284497
284823
  } else {
284498
- console.log(` ${import_picocolors18.default.dim("\u25CB")} ${label.padEnd(10)} ${import_picocolors18.default.dim("\u2014 no key")}`);
284824
+ console.log(` ${import_picocolors19.default.dim("\u25CB")} ${label.padEnd(10)} ${import_picocolors19.default.dim("\u2014 no key")}`);
284499
284825
  }
284500
284826
  }
284501
284827
  const plan = resolvePushPlan();
284502
284828
  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")})`)}`}`
284829
+ ` ${import_picocolors19.default.dim("push")} ${plan.apiKey ? `${import_picocolors19.default.bold(plan.flow)} ${import_picocolors19.default.dim(`with ${plan.apiKey.slice(0, 12)}\u2026 (${plan.source})`)}` : `${import_picocolors19.default.bold(plan.flow)} ${import_picocolors19.default.yellow("\u2014 no credential")} ${import_picocolors19.default.dim(`(run ${import_picocolors19.default.bold("ablo login")} or set ${import_picocolors19.default.bold("ABLO_API_KEY")})`)}`}`
284504
284830
  );
284505
284831
  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} `);
284832
+ if (capability.note) console.log(` ${import_picocolors19.default.dim(capability.note)}`);
284833
+ process.stdout.write(` ${import_picocolors19.default.dim("api")} ${apiUrl3} `);
284508
284834
  const reachable = await ping(apiUrl3);
284509
- console.log(reachable ? import_picocolors18.default.green("reachable") : import_picocolors18.default.red("unreachable"));
284835
+ console.log(reachable ? import_picocolors19.default.green("reachable") : import_picocolors19.default.red("unreachable"));
284510
284836
  const introspectKey = effective.key;
284511
284837
  const { source: dataSource, validation } = reachable ? await fetchRoutingState(apiUrl3, introspectKey) : { source: { kind: "unknown", detail: "unreachable" }, validation: null };
284512
284838
  if (dataSource.kind === "connected") {
284513
284839
  const how = [...new Set(dataSource.connections)].join(" + ");
284514
284840
  const pooled = detectPoolerIn(dataSource.hosts);
284515
284841
  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})`)}`);
284842
+ console.log(` ${import_picocolors19.default.dim("data")} ${import_picocolors19.default.green("\u2713")} ${import_picocolors19.default.dim(`database connected to this plane (${how})`)}`);
284517
284843
  if (pooled) {
284518
284844
  console.log(
284519
- ` ${import_picocolors18.default.yellow("\u26A0")} ${import_picocolors18.default.dim(
284845
+ ` ${import_picocolors19.default.yellow("\u26A0")} ${import_picocolors19.default.dim(
284520
284846
  `${pooled.host} is a connection pooler` + (pooled.direct ? `; register the direct host instead: ${pooled.direct}` : "; register the direct host instead")
284521
284847
  )}`
284522
284848
  );
284523
284849
  }
284524
284850
  if (unreachable) {
284525
- console.log(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.dim(`Ablo could not reach it \u2014 ${unreachable}`)}`);
284851
+ console.log(` ${import_picocolors19.default.red("\u2717")} ${import_picocolors19.default.dim(`Ablo could not reach it \u2014 ${unreachable}`)}`);
284526
284852
  }
284527
284853
  } else if (dataSource.kind === "none") {
284528
284854
  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")}`
284855
+ ` ${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
284856
  );
284531
284857
  } 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})`)}`);
284858
+ 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
284859
  }
284534
284860
  const pushed = reachable ? await fetchPushedSchema(apiUrl3, introspectKey) : null;
284535
284861
  if (reachable) {
284536
284862
  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}`);
284863
+ const when = pushed.pushedAt ? ` ${import_picocolors19.default.dim(`@ ${pushed.pushedAt.slice(0, 10)}`)}` : "";
284864
+ const ver = pushed.version != null ? ` ${import_picocolors19.default.dim(`(rev ${pushed.version})`)}` : "";
284865
+ const hashLabel = pushed.hash ? ` ${import_picocolors19.default.dim(`hash ${pushed.hash}`)}` : "";
284866
+ console.log(` ${import_picocolors19.default.dim("schema")} ${import_picocolors19.default.bold(`${pushed.models.length} models pushed`)}${ver}${hashLabel}${when}`);
284541
284867
  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}`);
284868
+ const tn = m2.typename === m2.key ? import_picocolors19.default.dim(`typename=${m2.typename}`) : import_picocolors19.default.yellow(`typename=${m2.typename}`);
284543
284869
  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}`);
284870
+ const conflictStr2 = conflict ? ` ${import_picocolors19.default.dim(`conflict=${conflict}`)}` : "";
284871
+ console.log(` ${import_picocolors19.default.dim("\u2022")} ${m2.key.padEnd(14)} ${tn}${conflictStr2}`);
284546
284872
  }
284547
284873
  } 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")})`)}`);
284874
+ 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
284875
  }
284550
284876
  }
284551
284877
  const drift = schemaDrift(await readLocalSchemaHash(), pushed?.hash);
284552
284878
  if (drift) {
284553
284879
  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})`)
284880
+ ` ${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
284881
  );
284556
284882
  }
284557
284883
  const found = blockers({
@@ -284563,33 +284889,33 @@ async function status(args = []) {
284563
284889
  });
284564
284890
  console.log();
284565
284891
  if (found.length > 0) {
284566
- console.log(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.bold("writes would fail right now")}`);
284892
+ console.log(` ${import_picocolors19.default.red("\u2717")} ${import_picocolors19.default.bold("writes would fail right now")}`);
284567
284893
  for (const b4 of found) {
284568
- console.log(` ${import_picocolors18.default.dim("\xB7")} ${b4.problem}`);
284569
- console.log(` ${import_picocolors18.default.dim(b4.fix)}`);
284894
+ console.log(` ${import_picocolors19.default.dim("\xB7")} ${b4.problem}`);
284895
+ console.log(` ${import_picocolors19.default.dim(b4.fix)}`);
284570
284896
  }
284571
284897
  } else if (dataSource.kind === "unknown") {
284572
284898
  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")}`
284899
+ ` ${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
284900
  );
284575
284901
  } else {
284576
- console.log(` ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.dim("ready \u2014 a write should succeed")}`);
284902
+ console.log(` ${import_picocolors19.default.green("\u2713")} ${import_picocolors19.default.dim("ready \u2014 a write should succeed")}`);
284577
284903
  }
284578
284904
  console.log();
284579
284905
  }
284580
284906
 
284581
284907
  // src/doctor.ts
284582
284908
  init_cjs_shims();
284583
- var import_picocolors19 = __toESM(require_picocolors(), 1);
284909
+ var import_picocolors20 = __toESM(require_picocolors(), 1);
284584
284910
  init_theme();
284585
284911
  init_controlPlane();
284586
284912
  init_config();
284587
284913
  init_target();
284588
284914
  init_readiness();
284589
284915
  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}`)}`);
284916
+ const mark = check2.state === "ok" ? import_picocolors20.default.green("\u2713") : check2.state === "fail" ? import_picocolors20.default.red("\u2717") : import_picocolors20.default.dim("\u2013");
284917
+ console.log(` ${mark} ${check2.label.padEnd(10)} ${check2.state === "fail" ? check2.detail : import_picocolors20.default.dim(check2.detail)}`);
284918
+ if (check2.fix) console.log(` ${" ".repeat(11)}${import_picocolors20.default.dim(`\u2192 ${check2.fix}`)}`);
284593
284919
  }
284594
284920
  async function ping2(apiUrl3) {
284595
284921
  const ctrl = new AbortController();
@@ -284607,7 +284933,7 @@ async function ping2(apiUrl3) {
284607
284933
  }
284608
284934
  async function doctor() {
284609
284935
  console.log(`
284610
- ${brand("ablo")} ${import_picocolors19.default.dim("doctor")}
284936
+ ${brand("ablo")} ${import_picocolors20.default.dim("doctor")}
284611
284937
  `);
284612
284938
  const apiUrl3 = apiBaseUrl();
284613
284939
  const effective = resolveEffectiveApiKey();
@@ -284722,15 +285048,15 @@ async function doctor() {
284722
285048
  console.log();
284723
285049
  if (failed > 0) {
284724
285050
  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` : "")
285051
+ ` ${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
285052
  );
284727
- console.log(import_picocolors19.default.dim(" Fix them in the order above \u2014 an earlier one often explains a later one."));
285053
+ console.log(import_picocolors20.default.dim(" Fix them in the order above \u2014 an earlier one often explains a later one."));
284728
285054
  } else if (skipped > 0) {
284729
285055
  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`)}`
285056
+ ` ${import_picocolors20.default.yellow("?")} ${import_picocolors20.default.dim(`nothing is blocking a write, but ${skipped} check${skipped === 1 ? "" : "s"} could not be run`)}`
284731
285057
  );
284732
285058
  } else {
284733
- console.log(` ${import_picocolors19.default.green("\u2713")} ${import_picocolors19.default.dim("everything checks out \u2014 a write should succeed")}`);
285059
+ console.log(` ${import_picocolors20.default.green("\u2713")} ${import_picocolors20.default.dim("everything checks out \u2014 a write should succeed")}`);
284734
285060
  }
284735
285061
  console.log();
284736
285062
  if (blocking.length > 0 || failed > 0) process.exitCode = 1;
@@ -284738,9 +285064,9 @@ async function doctor() {
284738
285064
 
284739
285065
  // src/logs.ts
284740
285066
  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);
285067
+ var import_errors16 = require("@abloatai/transaction/errors");
285068
+ var import_wire9 = require("@abloatai/transaction/wire");
285069
+ var import_picocolors21 = __toESM(require_picocolors(), 1);
284744
285070
  init_config();
284745
285071
  init_theme();
284746
285072
  init_controlPlane();
@@ -284783,12 +285109,12 @@ function parseLogsArgs(argv) {
284783
285109
  case "--mode": {
284784
285110
  const raw = argv[++i];
284785
285111
  const m2 = normalizeMode(raw);
284786
- if (!m2) throw new import_errors15.AbloValidationError(`--mode expects "sandbox" or "production", got "${raw}"`, { code: "cli_invalid_arguments" });
285112
+ if (!m2) throw new import_errors16.AbloValidationError(`--mode expects "sandbox" or "production", got "${raw}"`, { code: "cli_invalid_arguments" });
284787
285113
  args.mode = m2;
284788
285114
  break;
284789
285115
  }
284790
285116
  default:
284791
- throw new import_errors15.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285117
+ throw new import_errors16.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
284792
285118
  }
284793
285119
  }
284794
285120
  return args;
@@ -284807,10 +285133,10 @@ function resolveSince(since) {
284807
285133
  var sleep2 = (ms) => new Promise((r2) => setTimeout(r2, ms));
284808
285134
  function colorOp(op) {
284809
285135
  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);
285136
+ if (op === "create") return import_picocolors21.default.green(label);
285137
+ if (op === "update") return import_picocolors21.default.yellow(label);
285138
+ if (op === "delete") return import_picocolors21.default.red(label);
285139
+ return import_picocolors21.default.dim(label);
284814
285140
  }
284815
285141
  function render2(e2, json) {
284816
285142
  if (json) {
@@ -284819,21 +285145,21 @@ function render2(e2, json) {
284819
285145
  return;
284820
285146
  }
284821
285147
  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}`);
285148
+ const actor = e2.actor ? import_picocolors21.default.dim(` ${e2.actor}`) : "";
285149
+ console.log(` ${import_picocolors21.default.dim(t)} ${colorOp(e2.op)} ${import_picocolors21.default.bold(e2.model)} ${import_picocolors21.default.dim(e2.recordId)}${actor}`);
284824
285150
  }
284825
285151
  async function logs(argv) {
284826
285152
  let args;
284827
285153
  try {
284828
285154
  args = parseLogsArgs(argv);
284829
285155
  } catch (err) {
284830
- console.error(import_picocolors20.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285156
+ console.error(import_picocolors21.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284831
285157
  process.exit(1);
284832
285158
  }
284833
285159
  const apiKey = resolveApiKey(args.mode);
284834
285160
  if (!apiKey) {
284835
285161
  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")}.`)
285162
+ 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
285163
  );
284838
285164
  process.exit(1);
284839
285165
  }
@@ -284847,18 +285173,18 @@ async function logs(argv) {
284847
285173
  if (!res) return null;
284848
285174
  if (!res.ok) {
284849
285175
  const body = await res.json().catch(() => ({}));
284850
- console.error(import_picocolors20.default.red(` logs failed (${res.status}): ${body.reason ?? body.message ?? ""}`));
285176
+ console.error(import_picocolors21.default.red(` logs failed (${res.status}): ${body.reason ?? body.message ?? ""}`));
284851
285177
  process.exit(1);
284852
285178
  }
284853
285179
  const json = await res.json();
284854
285180
  return {
284855
285181
  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))
285182
+ 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
285183
  };
284858
285184
  }
284859
285185
  if (!args.json) {
284860
285186
  console.log(`
284861
- ${brand("ablo")} ${import_picocolors20.default.dim("logs")} ${import_picocolors20.default.dim(`(${args.mode ?? "active"} mode)`)}
285187
+ ${brand("ablo")} ${import_picocolors21.default.dim("logs")} ${import_picocolors21.default.dim(`(${args.mode ?? "active"} mode)`)}
284862
285188
  `);
284863
285189
  }
284864
285190
  const initial = await fetchPage({
@@ -284868,13 +285194,13 @@ async function logs(argv) {
284868
285194
  ...args.op ? { op: args.op } : {}
284869
285195
  });
284870
285196
  if (!initial) {
284871
- console.error(import_picocolors20.default.red(` Couldn't reach ${baseUrl2}.`));
285197
+ console.error(import_picocolors21.default.red(` Couldn't reach ${baseUrl2}.`));
284872
285198
  process.exit(1);
284873
285199
  }
284874
285200
  for (const e2 of initial.events) render2(e2, args.json);
284875
285201
  let cursor = initial.cursor;
284876
285202
  if (!args.follow) return;
284877
- if (!args.json) console.log(` ${import_picocolors20.default.dim("watching for new activity \u2026 (Ctrl-C to stop)")}
285203
+ if (!args.json) console.log(` ${import_picocolors21.default.dim("watching for new activity \u2026 (Ctrl-C to stop)")}
284878
285204
  `);
284879
285205
  for (; ; ) {
284880
285206
  await sleep2(1500);
@@ -284885,16 +285211,16 @@ async function logs(argv) {
284885
285211
  });
284886
285212
  if (!page) continue;
284887
285213
  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;
285214
+ const prev = (0, import_wire9.parseFeedCursor)(cursor);
285215
+ const next = (0, import_wire9.parseFeedCursor)(page.cursor);
285216
+ if (prev && next && (0, import_wire9.feedCursorAdvanced)(prev, next)) cursor = page.cursor;
284891
285217
  }
284892
285218
  }
284893
285219
 
284894
285220
  // src/webhooks.ts
284895
285221
  init_cjs_shims();
284896
285222
  var import_fs9 = require("fs");
284897
- var import_picocolors21 = __toESM(require_picocolors(), 1);
285223
+ var import_picocolors22 = __toESM(require_picocolors(), 1);
284898
285224
  var import_credentialPolicy4 = require("@abloatai/transaction/auth/credentialPolicy");
284899
285225
  init_config();
284900
285226
  init_theme();
@@ -284927,12 +285253,12 @@ function requireKey3(mode) {
284927
285253
  const apiKey = resolveApiKey(mode);
284928
285254
  if (!apiKey) {
284929
285255
  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")}.`)
285256
+ 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
285257
  );
284932
285258
  process.exit(1);
284933
285259
  }
284934
285260
  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_)."));
285261
+ console.error(import_picocolors22.default.red(" Managing webhooks requires a secret key ") + import_picocolors22.default.dim("(sk_test_ / sk_live_)."));
284936
285262
  process.exit(1);
284937
285263
  }
284938
285264
  return apiKey;
@@ -284948,12 +285274,12 @@ async function api(apiKey, method, path, body) {
284948
285274
  ...body ? { body: JSON.stringify(body) } : {}
284949
285275
  }).catch(() => null);
284950
285276
  if (!res) {
284951
- console.error(import_picocolors21.default.red(` Couldn't reach ${baseUrl()}.`));
285277
+ console.error(import_picocolors22.default.red(` Couldn't reach ${baseUrl()}.`));
284952
285278
  process.exit(1);
284953
285279
  }
284954
285280
  if (!res.ok) {
284955
285281
  const err = await res.json().catch(() => ({}));
284956
- console.error(import_picocolors21.default.red(` Request failed (${res.status}): ${err.message ?? err.reason ?? ""}`));
285282
+ console.error(import_picocolors22.default.red(` Request failed (${res.status}): ${err.message ?? err.reason ?? ""}`));
284957
285283
  process.exit(1);
284958
285284
  }
284959
285285
  return await res.json();
@@ -284975,11 +285301,11 @@ ${line}
284975
285301
  return file;
284976
285302
  }
284977
285303
  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}`);
285304
+ const dot = e2.status === "enabled" ? import_picocolors22.default.green("\u25CF") : import_picocolors22.default.red("\u25CF");
285305
+ const health = e2.last_error ? import_picocolors22.default.red(` last error: ${e2.last_error}`) : "";
285306
+ console.log(` ${dot} ${import_picocolors22.default.bold(e2.id)} ${e2.url}`);
284981
285307
  console.log(
284982
- import_picocolors21.default.dim(
285308
+ import_picocolors22.default.dim(
284983
285309
  ` ${e2.status} \xB7 ${e2.environment} \xB7 events ${e2.enabled_events.join(",")} \xB7 cursor ${e2.cursor ?? "\u2014"}${health}`
284984
285310
  )
284985
285311
  );
@@ -284991,7 +285317,7 @@ async function webhooks(argv) {
284991
285317
  if (sub === "create") {
284992
285318
  const url = positional(rest);
284993
285319
  if (!url) {
284994
- console.error(import_picocolors21.default.red(" Usage: ") + brand("ablo webhooks create <url>"));
285320
+ console.error(import_picocolors22.default.red(" Usage: ") + brand("ablo webhooks create <url>"));
284995
285321
  process.exit(1);
284996
285322
  }
284997
285323
  const apiKey = requireKey3(mode);
@@ -285003,8 +285329,8 @@ async function webhooks(argv) {
285003
285329
  });
285004
285330
  const file = writeSecretToEnv(created.secret);
285005
285331
  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)")}
285332
+ ${import_picocolors22.default.green("\u2713")} Registered ${import_picocolors22.default.bold(created.id)} \u2192 ${created.url}`);
285333
+ 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
285334
  `);
285009
285335
  return;
285010
285336
  }
@@ -285012,7 +285338,7 @@ async function webhooks(argv) {
285012
285338
  const apiKey = requireKey3(mode);
285013
285339
  const { data } = await api(apiKey, "GET", "");
285014
285340
  if (data.length === 0) {
285015
- console.log(import_picocolors21.default.dim(" No webhook endpoints. ") + brand("ablo webhooks create <url>"));
285341
+ console.log(import_picocolors22.default.dim(" No webhook endpoints. ") + brand("ablo webhooks create <url>"));
285016
285342
  return;
285017
285343
  }
285018
285344
  console.log();
@@ -285023,40 +285349,40 @@ async function webhooks(argv) {
285023
285349
  if (sub === "roll") {
285024
285350
  const id = positional(rest);
285025
285351
  if (!id) {
285026
- console.error(import_picocolors21.default.red(" Usage: ") + brand("ablo webhooks roll <id>"));
285352
+ console.error(import_picocolors22.default.red(" Usage: ") + brand("ablo webhooks roll <id>"));
285027
285353
  process.exit(1);
285028
285354
  }
285029
285355
  const apiKey = requireKey3(mode);
285030
285356
  const rolled = await api(apiKey, "POST", `/${id}/roll_secret`);
285031
285357
  const file = writeSecretToEnv(rolled.secret);
285032
285358
  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)")}
285359
+ ${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
285360
  `);
285035
285361
  return;
285036
285362
  }
285037
285363
  if (sub === "enable") {
285038
285364
  const id = positional(rest);
285039
285365
  if (!id) {
285040
- console.error(import_picocolors21.default.red(" Usage: ") + brand("ablo webhooks enable <id>"));
285366
+ console.error(import_picocolors22.default.red(" Usage: ") + brand("ablo webhooks enable <id>"));
285041
285367
  process.exit(1);
285042
285368
  }
285043
285369
  const apiKey = requireKey3(mode);
285044
285370
  const e2 = await api(apiKey, "POST", `/${id}/enable`);
285045
- console.log(` ${import_picocolors21.default.green("\u2713")} Re-enabled ${import_picocolors21.default.bold(e2.id)}`);
285371
+ console.log(` ${import_picocolors22.default.green("\u2713")} Re-enabled ${import_picocolors22.default.bold(e2.id)}`);
285046
285372
  return;
285047
285373
  }
285048
285374
  if (sub === "rm" || sub === "delete") {
285049
285375
  const id = positional(rest);
285050
285376
  if (!id) {
285051
- console.error(import_picocolors21.default.red(" Usage: ") + brand("ablo webhooks rm <id>"));
285377
+ console.error(import_picocolors22.default.red(" Usage: ") + brand("ablo webhooks rm <id>"));
285052
285378
  process.exit(1);
285053
285379
  }
285054
285380
  const apiKey = requireKey3(mode);
285055
285381
  await api(apiKey, "DELETE", `/${id}`);
285056
- console.log(` ${import_picocolors21.default.green("\u2713")} Removed ${import_picocolors21.default.bold(id)}`);
285382
+ console.log(` ${import_picocolors22.default.green("\u2713")} Removed ${import_picocolors22.default.bold(id)}`);
285057
285383
  return;
285058
285384
  }
285059
- console.log(` ${import_picocolors21.default.bold("Usage:")}`);
285385
+ console.log(` ${import_picocolors22.default.bold("Usage:")}`);
285060
285386
  console.log(` ${brand("ablo webhooks create <url>")} Register an endpoint; writes ${ENV_KEY}`);
285061
285387
  console.log(` ${brand("ablo webhooks list")} List endpoints + delivery health`);
285062
285388
  console.log(` ${brand("ablo webhooks roll <id>")} Mint a fresh signing secret`);
@@ -285067,8 +285393,8 @@ async function webhooks(argv) {
285067
285393
 
285068
285394
  // src/check.ts
285069
285395
  init_cjs_shims();
285070
- var import_errors16 = require("@abloatai/transaction/errors");
285071
- var import_picocolors22 = __toESM(require_picocolors(), 1);
285396
+ var import_errors17 = require("@abloatai/transaction/errors");
285397
+ var import_picocolors23 = __toESM(require_picocolors(), 1);
285072
285398
  init_src();
285073
285399
  var import_schema9 = require("@abloatai/transaction/schema");
285074
285400
  init_push();
@@ -285215,7 +285541,7 @@ function parseCheckArgs(argv) {
285215
285541
  appSchema = argv[++i] ?? appSchema;
285216
285542
  break;
285217
285543
  default:
285218
- throw new import_errors16.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285544
+ throw new import_errors17.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285219
285545
  }
285220
285546
  }
285221
285547
  return { schemaPath, exportName, appSchema };
@@ -285229,22 +285555,22 @@ function hostOf(connectionString) {
285229
285555
  }
285230
285556
  async function reportReadSubject(dbUrl) {
285231
285557
  const host = hostOf(dbUrl);
285232
- console.log(` ${import_picocolors22.default.dim("reading")} ${import_picocolors22.default.bold(host ?? "your database")}`);
285558
+ console.log(` ${import_picocolors23.default.dim("reading")} ${import_picocolors23.default.bold(host ?? "your database")}`);
285233
285559
  const effective = resolveEffectiveApiKey();
285234
285560
  const state = await fetchDataSourceState(apiBaseUrl(), effective.key);
285235
285561
  if (state.kind === "unknown") {
285236
285562
  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})`)}
285563
+ ` ${import_picocolors23.default.dim("ablo")} ${import_picocolors23.default.yellow("?")} ${import_picocolors23.default.dim(`couldn't ask which database Ablo reads (${state.detail})`)}
285238
285564
  `
285239
285565
  );
285240
285566
  return;
285241
285567
  }
285242
285568
  if (state.kind === "none") {
285243
285569
  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`
285570
+ ` ${import_picocolors23.default.dim("ablo")} ${import_picocolors23.default.yellow("!")} no database is registered for this plane, so Ablo does not read this one`
285245
285571
  );
285246
285572
  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.`)}
285573
+ ` ${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
285574
  `
285249
285575
  );
285250
285576
  return;
@@ -285252,15 +285578,15 @@ async function reportReadSubject(dbUrl) {
285252
285578
  const registered = [...new Set(state.hosts)];
285253
285579
  if (host && registered.length > 0 && !registered.includes(host)) {
285254
285580
  console.log(
285255
- ` ${import_picocolors22.default.dim("ablo")} ${import_picocolors22.default.yellow("!")} Ablo reads ${import_picocolors22.default.bold(registered.join(", "))}`
285581
+ ` ${import_picocolors23.default.dim("ablo")} ${import_picocolors23.default.yellow("!")} Ablo reads ${import_picocolors23.default.bold(registered.join(", "))}`
285256
285582
  );
285257
285583
  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.")}
285584
+ ` ${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
285585
  `
285260
285586
  );
285261
285587
  return;
285262
285588
  }
285263
- console.log(` ${import_picocolors22.default.dim("ablo")} ${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.dim("reads this database")}
285589
+ console.log(` ${import_picocolors23.default.dim("ablo")} ${import_picocolors23.default.green("\u2713")} ${import_picocolors23.default.dim("reads this database")}
285264
285590
  `);
285265
285591
  }
285266
285592
  async function check(argv) {
@@ -285268,20 +285594,20 @@ async function check(argv) {
285268
285594
  try {
285269
285595
  args = parseCheckArgs(argv);
285270
285596
  } catch (err) {
285271
- console.error(import_picocolors22.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285597
+ console.error(import_picocolors23.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285272
285598
  process.exit(1);
285273
285599
  }
285274
285600
  const dbUrl = readProjectAdminDatabaseUrl();
285275
285601
  if (!dbUrl) {
285276
285602
  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.`)
285603
+ 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
285604
  );
285279
285605
  process.exit(1);
285280
285606
  }
285281
285607
  const schema = await loadSchema(args.schemaPath, args.exportName);
285282
285608
  const schemaJson = JSON.parse((0, import_schema9.serializeSchema)(schema));
285283
285609
  console.log(`
285284
- ${brand("ablo")} ${import_picocolors22.default.dim("check")} ${import_picocolors22.default.dim(`schema "${args.appSchema}"`)}
285610
+ ${brand("ablo")} ${import_picocolors23.default.dim("check")} ${import_picocolors23.default.dim(`schema "${args.appSchema}"`)}
285285
285611
  `);
285286
285612
  await reportReadSubject(dbUrl);
285287
285613
  const sql = src_default(dbUrl, { max: 1, prepare: false, onnotice: () => {
@@ -285293,7 +285619,7 @@ async function check(argv) {
285293
285619
  [args.appSchema]
285294
285620
  );
285295
285621
  } catch (err) {
285296
- console.error(import_picocolors22.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
285622
+ console.error(import_picocolors23.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
285297
285623
  await sql.end({ timeout: 2 });
285298
285624
  process.exit(1);
285299
285625
  }
@@ -285315,7 +285641,7 @@ async function check(argv) {
285315
285641
  declaredTables.add(table);
285316
285642
  const present = colsByTable.get(table);
285317
285643
  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")}`);
285644
+ 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
285645
  errors++;
285320
285646
  continue;
285321
285647
  }
@@ -285337,26 +285663,26 @@ async function check(argv) {
285337
285663
  if (!present.has(col)) problems.push(`missing column "${col}" (field ${fieldName})`);
285338
285664
  }
285339
285665
  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}`);
285666
+ console.log(` ${import_picocolors23.default.red("\u2717")} ${import_picocolors23.default.bold(key)} ${import_picocolors23.default.dim("\u2192")} ${table}`);
285667
+ for (const p2 of problems) console.log(` ${import_picocolors23.default.red("\u2022")} ${p2}`);
285668
+ for (const w2 of warns) console.log(` ${import_picocolors23.default.yellow("\u2022")} ${w2}`);
285343
285669
  errors++;
285344
285670
  } 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}`);
285671
+ console.log(` ${import_picocolors23.default.yellow("!")} ${import_picocolors23.default.bold(key)} ${import_picocolors23.default.dim("\u2192")} ${table}`);
285672
+ for (const w2 of warns) console.log(` ${import_picocolors23.default.yellow("\u2022")} ${w2}`);
285347
285673
  warnings++;
285348
285674
  } 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)`)}`);
285675
+ console.log(` ${import_picocolors23.default.green("\u2713")} ${import_picocolors23.default.bold(key)} ${import_picocolors23.default.dim(`\u2192 ${table} (id, ${orgCol ?? "no org"} ok)`)}`);
285350
285676
  }
285351
285677
  }
285352
285678
  const modelCount = Object.keys(schemaJson.models).length;
285353
285679
  const ignored = [...colsByTable.keys()].filter((t) => !declaredTables.has(t)).length;
285354
285680
  console.log(
285355
285681
  `
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"}`)}` : "")
285682
+ ${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
285683
  );
285358
285684
  if (ignored > 0) {
285359
- console.log(` ${import_picocolors22.default.dim(`${ignored} other table${ignored === 1 ? "" : "s"} in your database \u2014 ignored by Ablo`)}`);
285685
+ console.log(` ${import_picocolors23.default.dim(`${ignored} other table${ignored === 1 ? "" : "s"} in your database \u2014 ignored by Ablo`)}`);
285360
285686
  }
285361
285687
  console.log();
285362
285688
  process.exit(errors > 0 ? 1 : 0);
@@ -285367,7 +285693,7 @@ init_dbRole();
285367
285693
 
285368
285694
  // src/upgrade.ts
285369
285695
  init_cjs_shims();
285370
- var import_picocolors23 = __toESM(require_picocolors(), 1);
285696
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
285371
285697
  var import_ts_morph = __toESM(require_ts_morph(), 1);
285372
285698
  var DEFAULT_GLOBS = ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}", "ablo/**/*.{ts,tsx}", "lib/**/*.{ts,tsx}"];
285373
285699
  var VERB_ARGS = {
@@ -285445,7 +285771,7 @@ async function upgrade(argv) {
285445
285771
  project.addSourceFilesAtPaths(globs.length > 0 ? globs : DEFAULT_GLOBS);
285446
285772
  const files = project.getSourceFiles();
285447
285773
  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"`.'));
285774
+ console.log(import_picocolors24.default.yellow(' No .ts/.tsx files found. Pass a glob, e.g. `ablo upgrade "src/**/*.tsx"`.'));
285449
285775
  return;
285450
285776
  }
285451
285777
  const edits = [];
@@ -285519,39 +285845,39 @@ async function upgrade(argv) {
285519
285845
  const rel = (f) => f.replace(cwd + "/", "");
285520
285846
  console.log();
285521
285847
  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."));
285848
+ console.log(import_picocolors24.default.green(" \u2713 Nothing to migrate \u2014 your code is already on the current API."));
285523
285849
  return;
285524
285850
  }
285525
285851
  if (edits.length > 0) {
285526
- console.log(import_picocolors23.default.bold(` ${write ? "Applied" : "Would apply"} ${edits.length} change${edits.length === 1 ? "" : "s"}:`));
285852
+ console.log(import_picocolors24.default.bold(` ${write ? "Applied" : "Would apply"} ${edits.length} change${edits.length === 1 ? "" : "s"}:`));
285527
285853
  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}`);
285854
+ console.log(` ${import_picocolors24.default.dim(`${rel(e2.file)}:${e2.line}`)} ${import_picocolors24.default.cyan(e2.rule)}`);
285855
+ console.log(` ${import_picocolors24.default.red("-")} ${e2.before}`);
285856
+ console.log(` ${import_picocolors24.default.green("+")} ${e2.after}`);
285531
285857
  }
285532
285858
  }
285533
285859
  if (manual.length > 0) {
285534
285860
  console.log();
285535
- console.log(import_picocolors23.default.bold(import_picocolors23.default.yellow(` ${manual.length} spot${manual.length === 1 ? "" : "s"} need manual review (structural):`)));
285861
+ console.log(import_picocolors24.default.bold(import_picocolors24.default.yellow(` ${manual.length} spot${manual.length === 1 ? "" : "s"} need manual review (structural):`)));
285536
285862
  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)}`);
285863
+ console.log(` ${import_picocolors24.default.dim(`${rel(m2.file)}:${m2.line}`)} ${import_picocolors24.default.yellow(m2.rule)}`);
285864
+ console.log(` ${import_picocolors24.default.dim(m2.snippet)}`);
285539
285865
  console.log(` \u2192 ${m2.hint}`);
285540
285866
  }
285541
285867
  }
285542
285868
  console.log();
285543
285869
  if (write) {
285544
285870
  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.`));
285871
+ console.log(import_picocolors24.default.green(` \u2713 Wrote ${edits.length} change${edits.length === 1 ? "" : "s"}. Review the diff, run your typecheck.`));
285546
285872
  } 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)."));
285873
+ 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
285874
  }
285549
285875
  }
285550
285876
 
285551
285877
  // src/pull.ts
285552
285878
  init_cjs_shims();
285553
- var import_errors17 = require("@abloatai/transaction/errors");
285554
- var import_picocolors24 = __toESM(require_picocolors(), 1);
285879
+ var import_errors18 = require("@abloatai/transaction/errors");
285880
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
285555
285881
  init_src();
285556
285882
  var import_fs10 = require("fs");
285557
285883
  init_theme();
@@ -285579,7 +285905,7 @@ function parsePullArgs(argv) {
285579
285905
  force = true;
285580
285906
  break;
285581
285907
  default:
285582
- throw new import_errors17.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285908
+ throw new import_errors18.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285583
285909
  }
285584
285910
  }
285585
285911
  return { out, appSchema, importPath, force };
@@ -285649,56 +285975,56 @@ async function pull(argv) {
285649
285975
  try {
285650
285976
  args = parsePullArgs(argv);
285651
285977
  } catch (err) {
285652
- console.error(import_picocolors24.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285978
+ console.error(import_picocolors25.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285653
285979
  process.exit(1);
285654
285980
  }
285655
285981
  const dbUrl = readProjectAdminDatabaseUrl();
285656
285982
  if (!dbUrl) {
285657
285983
  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.`)
285984
+ 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
285985
  );
285660
285986
  process.exit(1);
285661
285987
  }
285662
285988
  if ((0, import_fs10.existsSync)(args.out) && !args.force) {
285663
285989
  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.`)
285990
+ import_picocolors25.default.red(` ${args.out} already exists.`) + import_picocolors25.default.dim(` Re-run with ${import_picocolors25.default.bold("--force")} to overwrite.`)
285665
285991
  );
285666
285992
  process.exit(1);
285667
285993
  }
285668
285994
  console.log(`
285669
- ${brand("ablo")} ${import_picocolors24.default.dim("pull")} ${import_picocolors24.default.dim(`schema "${args.appSchema}"`)}
285995
+ ${brand("ablo")} ${import_picocolors25.default.dim("pull")} ${import_picocolors25.default.dim(`schema "${args.appSchema}"`)}
285670
285996
  `);
285671
285997
  let result;
285672
285998
  try {
285673
285999
  result = await buildSchemaSourceFromDb({ dbUrl, appSchema: args.appSchema, importPath: args.importPath });
285674
286000
  } catch (err) {
285675
- console.error(import_picocolors24.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
286001
+ console.error(import_picocolors25.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
285676
286002
  process.exit(1);
285677
286003
  }
285678
286004
  if (result.models.length === 0) {
285679
286005
  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).`)
286006
+ 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
286007
  );
285682
286008
  process.exit(1);
285683
286009
  }
285684
286010
  (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(", ")}`)}`);
286011
+ console.log(` ${import_picocolors25.default.green("\u2713")} wrote ${import_picocolors25.default.bold(args.out)} ${import_picocolors25.default.dim(`(${result.models.length} models)`)}`);
286012
+ console.log(` ${import_picocolors25.default.dim(`models: ${result.models.join(", ")}`)}`);
285687
286013
  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}`)}`);
286014
+ console.log(` ${import_picocolors25.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
286015
+ for (const s of result.skipped) console.log(` ${import_picocolors25.default.dim(`- ${s.name}: ${s.reason}`)}`);
285690
286016
  }
285691
286017
  console.log(
285692
286018
  `
285693
- ${import_picocolors24.default.dim("Introspection is lossy (enums, JSON shape, relations). Review the file, then")} ${import_picocolors24.default.bold("ablo check")}.
286019
+ ${import_picocolors25.default.dim("Introspection is lossy (enums, JSON shape, relations). Review the file, then")} ${import_picocolors25.default.bold("ablo check")}.
285694
286020
  `
285695
286021
  );
285696
286022
  }
285697
286023
 
285698
286024
  // src/prismaPull.ts
285699
286025
  init_cjs_shims();
285700
- var import_errors18 = require("@abloatai/transaction/errors");
285701
- var import_picocolors25 = __toESM(require_picocolors(), 1);
286026
+ var import_errors19 = require("@abloatai/transaction/errors");
286027
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
285702
286028
  var import_fs11 = require("fs");
285703
286029
  init_theme();
285704
286030
  var DEFAULT_SCHEMA = "prisma/schema.prisma";
@@ -285895,7 +286221,7 @@ function parsePrismaPullArgs(argv) {
285895
286221
  force = true;
285896
286222
  break;
285897
286223
  default:
285898
- if (arg.startsWith("--")) throw new import_errors18.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
286224
+ if (arg.startsWith("--")) throw new import_errors19.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
285899
286225
  schema = arg;
285900
286226
  }
285901
286227
  }
@@ -285906,56 +286232,56 @@ async function prismaPull(argv) {
285906
286232
  try {
285907
286233
  args = parsePrismaPullArgs(argv);
285908
286234
  } catch (err) {
285909
- console.error(import_picocolors25.default.red(` ${err instanceof Error ? err.message : String(err)}`));
286235
+ console.error(import_picocolors26.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285910
286236
  process.exit(1);
285911
286237
  }
285912
286238
  if (!(0, import_fs11.existsSync)(args.schema)) {
285913
286239
  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>")}.`)
286240
+ 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
286241
  );
285916
286242
  process.exit(1);
285917
286243
  }
285918
286244
  if ((0, import_fs11.existsSync)(args.out) && !args.force) {
285919
286245
  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.`)
286246
+ import_picocolors26.default.red(` ${args.out} already exists.`) + import_picocolors26.default.dim(` Re-run with ${import_picocolors26.default.bold("--force")} to overwrite.`)
285921
286247
  );
285922
286248
  process.exit(1);
285923
286249
  }
285924
286250
  console.log(`
285925
- ${brand("ablo")} ${import_picocolors25.default.dim("pull prisma")} ${import_picocolors25.default.dim(args.schema)}
286251
+ ${brand("ablo")} ${import_picocolors26.default.dim("pull prisma")} ${import_picocolors26.default.dim(args.schema)}
285926
286252
  `);
285927
286253
  let result;
285928
286254
  try {
285929
286255
  const src = (0, import_fs11.readFileSync)(args.schema, "utf8");
285930
286256
  result = buildSchemaSourceFromPrisma({ src, importPath: args.importPath });
285931
286257
  } catch (err) {
285932
- console.error(import_picocolors25.default.red(` Couldn't parse the schema: ${err instanceof Error ? err.message : String(err)}`));
286258
+ console.error(import_picocolors26.default.red(` Couldn't parse the schema: ${err instanceof Error ? err.message : String(err)}`));
285933
286259
  process.exit(1);
285934
286260
  }
285935
286261
  if (result.models.length === 0) {
285936
286262
  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")}).`)
286263
+ 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
286264
  );
285939
286265
  process.exit(1);
285940
286266
  }
285941
286267
  (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(", ")}`)}`);
286268
+ console.log(` ${import_picocolors26.default.green("\u2713")} wrote ${import_picocolors26.default.bold(args.out)} ${import_picocolors26.default.dim(`(${result.models.length} models)`)}`);
286269
+ console.log(` ${import_picocolors26.default.dim(`models: ${result.models.join(", ")}`)}`);
285944
286270
  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}`)}`);
286271
+ console.log(` ${import_picocolors26.default.dim(`${result.skipped.length} model(s) skipped:`)}`);
286272
+ for (const s of result.skipped) console.log(` ${import_picocolors26.default.dim(`- ${s.name}: ${s.reason}`)}`);
285947
286273
  }
285948
286274
  console.log(
285949
286275
  `
285950
- ${import_picocolors25.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors25.default.bold("ablo check")}.
286276
+ ${import_picocolors26.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors26.default.bold("ablo check")}.
285951
286277
  `
285952
286278
  );
285953
286279
  }
285954
286280
 
285955
286281
  // src/drizzlePull.ts
285956
286282
  init_cjs_shims();
285957
- var import_picocolors26 = __toESM(require_picocolors(), 1);
285958
- var import_errors19 = require("@abloatai/transaction/errors");
286283
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
286284
+ var import_errors20 = require("@abloatai/transaction/errors");
285959
286285
  var import_fs12 = require("fs");
285960
286286
  var import_path7 = require("path");
285961
286287
  init_theme();
@@ -286062,7 +286388,7 @@ function parseDrizzlePullArgs(argv) {
286062
286388
  force = true;
286063
286389
  break;
286064
286390
  default:
286065
- if (arg.startsWith("--")) throw new import_errors19.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
286391
+ if (arg.startsWith("--")) throw new import_errors20.AbloValidationError(`unknown flag: ${arg}`, { code: "cli_invalid_arguments" });
286066
286392
  schema = arg;
286067
286393
  }
286068
286394
  }
@@ -286079,27 +286405,27 @@ async function drizzlePull(argv) {
286079
286405
  try {
286080
286406
  args = parseDrizzlePullArgs(argv);
286081
286407
  } catch (err) {
286082
- console.error(import_picocolors26.default.red(` ${err instanceof Error ? err.message : String(err)}`));
286408
+ console.error(import_picocolors27.default.red(` ${err instanceof Error ? err.message : String(err)}`));
286083
286409
  process.exit(1);
286084
286410
  }
286085
286411
  if (!args.schema) {
286086
286412
  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")}.`)
286413
+ 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
286414
  );
286089
286415
  process.exit(1);
286090
286416
  }
286091
286417
  if (!(0, import_fs12.existsSync)(args.schema)) {
286092
- console.error(import_picocolors26.default.red(` No file at ${import_picocolors26.default.bold(args.schema)}.`));
286418
+ console.error(import_picocolors27.default.red(` No file at ${import_picocolors27.default.bold(args.schema)}.`));
286093
286419
  process.exit(1);
286094
286420
  }
286095
286421
  if ((0, import_fs12.existsSync)(args.out) && !args.force) {
286096
286422
  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.`)
286423
+ import_picocolors27.default.red(` ${args.out} already exists.`) + import_picocolors27.default.dim(` Re-run with ${import_picocolors27.default.bold("--force")} to overwrite.`)
286098
286424
  );
286099
286425
  process.exit(1);
286100
286426
  }
286101
286427
  console.log(`
286102
- ${brand("ablo")} ${import_picocolors26.default.dim("pull drizzle")} ${import_picocolors26.default.dim(args.schema)}
286428
+ ${brand("ablo")} ${import_picocolors27.default.dim("pull drizzle")} ${import_picocolors27.default.dim(args.schema)}
286103
286429
  `);
286104
286430
  let result;
286105
286431
  try {
@@ -286107,26 +286433,26 @@ async function drizzlePull(argv) {
286107
286433
  result = await buildSchemaSourceFromDrizzle({ mod, importPath: args.importPath });
286108
286434
  } catch (err) {
286109
286435
  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);
286436
+ const hint = msg.includes("Cannot find package 'drizzle-orm'") ? import_picocolors27.default.dim(` (install ${import_picocolors27.default.bold("drizzle-orm")} in this project)`) : "";
286437
+ console.error(import_picocolors27.default.red(` Couldn't load the schema: ${msg}`) + hint);
286112
286438
  process.exit(1);
286113
286439
  }
286114
286440
  if (result.models.length === 0) {
286115
286441
  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).`)
286442
+ 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
286443
  );
286118
286444
  process.exit(1);
286119
286445
  }
286120
286446
  (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(", ")}`)}`);
286447
+ console.log(` ${import_picocolors27.default.green("\u2713")} wrote ${import_picocolors27.default.bold(args.out)} ${import_picocolors27.default.dim(`(${result.models.length} models)`)}`);
286448
+ console.log(` ${import_picocolors27.default.dim(`models: ${result.models.join(", ")}`)}`);
286123
286449
  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}`)}`);
286450
+ console.log(` ${import_picocolors27.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
286451
+ for (const s of result.skipped) console.log(` ${import_picocolors27.default.dim(`- ${s.name}: ${s.reason}`)}`);
286126
286452
  }
286127
286453
  console.log(
286128
286454
  `
286129
- ${import_picocolors26.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors26.default.bold("ablo check")}.
286455
+ ${import_picocolors27.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors27.default.bold("ablo check")}.
286130
286456
  `
286131
286457
  );
286132
286458
  }
@@ -286212,7 +286538,7 @@ async function getCurrentUser(): Promise<{ id: string } | null> {
286212
286538
 
286213
286539
  // src/index.ts
286214
286540
  var LOGO = `
286215
- ${brand("ablo")} ${import_picocolors27.default.dim("sync engine")}
286541
+ ${brand("ablo")} ${import_picocolors28.default.dim("sync engine")}
286216
286542
  `;
286217
286543
  var HANDLERS = {
286218
286544
  init: (argv) => init([...argv]),
@@ -286221,6 +286547,7 @@ var HANDLERS = {
286221
286547
  projects: (argv) => projects([...argv]),
286222
286548
  branch: (argv) => branches([...argv]),
286223
286549
  status: (argv) => status([...argv]),
286550
+ whoami: (argv) => whoami([...argv]),
286224
286551
  doctor: () => doctor(),
286225
286552
  logs: (argv) => logs([...argv]),
286226
286553
  webhooks: (argv) => webhooks([...argv]),
@@ -286239,7 +286566,7 @@ async function runDev(argv) {
286239
286566
  const devArgs = [...argv];
286240
286567
  const oneShot = devArgs.includes("--no-watch");
286241
286568
  console.log(
286242
- import_picocolors27.default.dim(
286569
+ import_picocolors28.default.dim(
286243
286570
  oneShot ? " `ablo dev --no-watch` prepares this Git branch and pushes once." : " `ablo dev` prepares this Git branch and watches the schema."
286244
286571
  )
286245
286572
  );
@@ -286260,13 +286587,13 @@ async function runPush2(argv) {
286260
286587
  const guard = guardActiveProjectKey();
286261
286588
  if (!guard.ok && guard.available.length > 0 && !rest.includes("--url")) {
286262
286589
  console.error(
286263
- ` ${import_picocolors27.default.yellow("\u26A0")} active project ${import_picocolors27.default.bold(guard.activeProfile)} has no stored key ${import_picocolors27.default.dim(
286590
+ ` ${import_picocolors28.default.yellow("\u26A0")} active project ${import_picocolors28.default.bold(guard.activeProfile)} has no stored key ${import_picocolors28.default.dim(
286264
286591
  `(you have keys for: ${guard.available.join(", ")})`
286265
286592
  )}`
286266
286593
  );
286267
286594
  const loginCmd = guard.activeProfile === "default" ? "ablo login" : `ablo login --project ${guard.activeProfile}`;
286268
286595
  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>")}.`)
286596
+ import_picocolors28.default.dim(` Mint one with ${import_picocolors28.default.bold(loginCmd)}, or switch with ${import_picocolors28.default.bold("ablo projects use <slug>")}.`)
286270
286597
  );
286271
286598
  process.exitCode = 1;
286272
286599
  return;
@@ -286277,7 +286604,7 @@ async function runPush2(argv) {
286277
286604
  }
286278
286605
  function runRenamedSchema(argv) {
286279
286606
  const forwarded = argv.slice(1).join(" ");
286280
- console.error(` ${import_picocolors27.default.red("\u2717")} \`ablo schema push\` was renamed to \`${brand("ablo push")}\`.`);
286607
+ console.error(` ${import_picocolors28.default.red("\u2717")} \`ablo schema push\` was renamed to \`${brand("ablo push")}\`.`);
286281
286608
  console.error(` Run \`ablo push${forwarded ? " " + forwarded : ""}\` instead.`);
286282
286609
  process.exitCode = 1;
286283
286610
  }
@@ -286287,7 +286614,7 @@ async function main() {
286287
286614
  const argv = process.argv.slice(3);
286288
286615
  if (!command && raw !== void 0 && raw !== "help" && !raw.startsWith("-")) {
286289
286616
  const suggestion = suggestCommand(raw);
286290
- throw new import_errors20.AbloValidationError(
286617
+ throw new import_errors21.AbloValidationError(
286291
286618
  `\`${raw}\` isn't an ablo command.` + (suggestion ? ` Did you mean \`ablo ${suggestion}\`?` : " Run `ablo help --all` to see every command."),
286292
286619
  { code: "cli_invalid_arguments" }
286293
286620
  );
@@ -286318,7 +286645,7 @@ function printCoreHelp() {
286318
286645
  const width = Math.max(...rows.map((r2) => r2.run.length)) + 4;
286319
286646
  console.log(LOGO);
286320
286647
  for (const group of CORE_GROUPS) {
286321
- console.log(` ${import_picocolors27.default.bold(group)}`);
286648
+ console.log(` ${import_picocolors28.default.bold(group)}`);
286322
286649
  const printed = group === "More" ? [...coreRows(group), ...extra] : coreRows(group);
286323
286650
  for (const row of printed) console.log(` npx ablo ${row.run.padEnd(width)}${row.does}`);
286324
286651
  console.log();
@@ -286327,7 +286654,7 @@ function printCoreHelp() {
286327
286654
  }
286328
286655
  function printSchemaReminder() {
286329
286656
  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(".")
286657
+ 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
286658
  );
286332
286659
  console.log();
286333
286660
  }
@@ -286337,7 +286664,7 @@ function printFullHelp() {
286337
286664
  ) + 2;
286338
286665
  console.log(LOGO);
286339
286666
  for (const group of FULL_GROUPS) {
286340
- console.log(` ${import_picocolors27.default.bold(group)}`);
286667
+ console.log(` ${import_picocolors28.default.bold(group)}`);
286341
286668
  for (const row of fullRows(group)) {
286342
286669
  console.log(row.does === void 0 ? ` ${" ".repeat(9)}${row.run}` : ` npx ablo ${row.run.padEnd(width)}${row.does}`);
286343
286670
  }
@@ -286392,7 +286719,7 @@ async function ensureInitProject(opts) {
286392
286719
  const ensured = await ensureProject(slug);
286393
286720
  if (ensured) {
286394
286721
  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.`
286722
+ ` ${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
286723
  );
286397
286724
  }
286398
286725
  }
@@ -286434,7 +286761,7 @@ async function chooseBool(flagValue, fallback, interactive, prompt) {
286434
286761
  async function init(args = []) {
286435
286762
  const opts = parseInitArgs(args);
286436
286763
  const interactive = Boolean(process.stdin.isTTY) && !opts.yes && !process.env.CI;
286437
- Ie(`${brand("ablo")} ${import_picocolors27.default.dim("sync engine")}`);
286764
+ Ie(`${brand("ablo")} ${import_picocolors28.default.dim("sync engine")}`);
286438
286765
  if (!(0, import_fs13.existsSync)("package.json")) {
286439
286766
  xe("No package.json found. Run this from your project root.");
286440
286767
  process.exit(1);
@@ -286512,7 +286839,7 @@ async function init(args = []) {
286512
286839
  if (pullExisting) {
286513
286840
  const dbUrl = readProjectAdminDatabaseUrl();
286514
286841
  if (!dbUrl) {
286515
- schemaNote = import_picocolors27.default.dim(` (no ${ADMIN_URL_VAR} \u2014 wrote starter; run \`ablo pull\` later)`);
286842
+ schemaNote = import_picocolors28.default.dim(` (no ${ADMIN_URL_VAR} \u2014 wrote starter; run \`ablo pull\` later)`);
286516
286843
  } else {
286517
286844
  try {
286518
286845
  const pulled = await buildSchemaSourceFromDb({
@@ -286522,12 +286849,12 @@ async function init(args = []) {
286522
286849
  });
286523
286850
  if (pulled.models.length > 0) {
286524
286851
  schemaSource = pulled.source;
286525
- schemaNote = import_picocolors27.default.dim(` (pulled ${pulled.models.length} models)`);
286852
+ schemaNote = import_picocolors28.default.dim(` (pulled ${pulled.models.length} models)`);
286526
286853
  } else {
286527
- schemaNote = import_picocolors27.default.dim(" (no adoptable tables \u2014 wrote starter)");
286854
+ schemaNote = import_picocolors28.default.dim(" (no adoptable tables \u2014 wrote starter)");
286528
286855
  }
286529
286856
  } catch {
286530
- schemaNote = import_picocolors27.default.dim(" (pull failed \u2014 wrote starter)");
286857
+ schemaNote = import_picocolors28.default.dim(" (pull failed \u2014 wrote starter)");
286531
286858
  }
286532
286859
  }
286533
286860
  }
@@ -286551,9 +286878,9 @@ async function init(args = []) {
286551
286878
  const existing = (0, import_fs13.readFileSync)(envFile, "utf-8");
286552
286879
  if (!existing.includes("ABLO_")) {
286553
286880
  (0, import_fs13.writeFileSync)(envFile, existing + "\n" + envBody);
286554
- created.push(`${envFile} ${import_picocolors27.default.dim("(appended)")}`);
286881
+ created.push(`${envFile} ${import_picocolors28.default.dim("(appended)")}`);
286555
286882
  } else {
286556
- created.push(`${envFile} ${import_picocolors27.default.dim("(already configured)")}`);
286883
+ created.push(`${envFile} ${import_picocolors28.default.dim("(already configured)")}`);
286557
286884
  }
286558
286885
  }
286559
286886
  if (agent) {
@@ -286569,17 +286896,17 @@ async function init(args = []) {
286569
286896
  }
286570
286897
  const providersPath = (0, import_path8.join)(layout.appBase, "providers.tsx");
286571
286898
  (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>)`)}`);
286899
+ created.push(`${providersPath} ${import_picocolors28.default.dim(`(wrap ${(0, import_path8.join)(layout.appBase, "layout.tsx")} in <Providers>)`)}`);
286573
286900
  const sessionDir = (0, import_path8.join)(layout.appBase, "api", "ablo-session");
286574
286901
  (0, import_fs13.mkdirSync)(sessionDir, { recursive: true });
286575
286902
  (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)")}`);
286903
+ created.push(`${(0, import_path8.join)(sessionDir, "route.ts")} ${import_picocolors28.default.dim("(wire your auth)")}`);
286577
286904
  }
286578
286905
  if (framework !== "vanilla") {
286579
286906
  (0, import_fs13.writeFileSync)((0, import_path8.join)(abloDir, "TaskList.tsx"), generateComponent());
286580
286907
  created.push(`${abloDir}/TaskList.tsx`);
286581
286908
  }
286582
- Me(created.map((f) => `${import_picocolors27.default.green("\u2713")} ${f}`).join("\n"), "Created");
286909
+ Me(created.map((f) => `${import_picocolors28.default.green("\u2713")} ${f}`).join("\n"), "Created");
286583
286910
  const pm = detectPackageManager();
286584
286911
  if (opts.install) {
286585
286912
  const s = Y2();
@@ -286588,46 +286915,46 @@ async function init(args = []) {
286588
286915
  (0, import_child_process3.execSync)(`${pm} add @abloatai/ablo`, { stdio: "ignore" });
286589
286916
  s.stop("Installed @abloatai/ablo");
286590
286917
  } catch {
286591
- s.stop(`${import_picocolors27.default.yellow("!")} Couldn't auto-install \u2014 run ${import_picocolors27.default.bold(`${pm} install @abloatai/ablo`)}`);
286918
+ s.stop(`${import_picocolors28.default.yellow("!")} Couldn't auto-install \u2014 run ${import_picocolors28.default.bold(`${pm} install @abloatai/ablo`)}`);
286592
286919
  }
286593
286920
  }
286594
286921
  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`,
286922
+ `Run ${import_picocolors28.default.bold("npx ablo login")} to authorize branch management`,
286923
+ `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`,
286924
+ `Run ${import_picocolors28.default.bold("npx ablo dev")} \u2014 pushes your schema definition and watches for changes`,
286598
286925
  ...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`
286926
+ `Connect your database \u2014 ${import_picocolors28.default.bold("npx ablo connect")} prints the one-time logical-replication setup SQL to run on your Postgres`,
286927
+ `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`,
286928
+ `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
286929
  ] : [
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")}`
286930
+ `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
286931
  ],
286605
286932
  ...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"))}`
286933
+ `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
286934
  ] : [],
286608
- `Run ${import_picocolors27.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
286935
+ `Run ${import_picocolors28.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
286609
286936
  ...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`
286937
+ `Run ${import_picocolors28.default.bold(`npx tsx ${abloDir}/agent.ts`)} \u2014 an AI teammate edits the same tasks`,
286938
+ `Run ${import_picocolors28.default.bold("npx ablo logs")} to watch human + agent commits stream by`
286612
286939
  ] : []
286613
286940
  ];
286614
286941
  Me(steps.map((s, i) => `${i + 1}. ${s}`).join("\n"), "Next steps");
286615
286942
  const existingKey = resolveManagementKey();
286616
286943
  if (existingKey) {
286617
286944
  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`);
286945
+ 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
286946
  return;
286620
286947
  }
286621
286948
  if (interactive && opts.login) {
286622
286949
  const loginNow = await ye({ message: "Log in now? (opens your browser)", initialValue: true });
286623
286950
  if (!pD(loginNow) && loginNow) {
286624
- Se(`${import_picocolors27.default.dim("Docs:")} https://abloatai.com/docs`);
286951
+ Se(`${import_picocolors28.default.dim("Docs:")} https://abloatai.com/docs`);
286625
286952
  await login();
286626
286953
  await ensureInitProject(opts);
286627
286954
  return;
286628
286955
  }
286629
286956
  }
286630
- Se(`Run ${import_picocolors27.default.bold("npx ablo login")} when ready. ${import_picocolors27.default.dim("Docs:")} https://abloatai.com/docs`);
286957
+ Se(`Run ${import_picocolors28.default.bold("npx ablo login")} when ready. ${import_picocolors28.default.dim("Docs:")} https://abloatai.com/docs`);
286631
286958
  }
286632
286959
  function generateSchema() {
286633
286960
  return `import { defineSchema, model, relation, z } from '@abloatai/ablo/schema';