@abloatai/ablo 0.34.0 → 0.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -787,8 +787,8 @@ ${import_picocolors2.default.gray(d2)} ${t}
787
787
  if (i) process.stdout.write(`${I2} ${l2}...`);
788
788
  else if (t === "timer") process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
789
789
  else {
790
- const z8 = ".".repeat(Math.floor(w2)).slice(0, 3);
791
- process.stdout.write(`${I2} ${l2}${z8}`);
790
+ const z9 = ".".repeat(Math.floor(w2)).slice(0, 3);
791
+ process.stdout.write(`${I2} ${l2}${z9}`);
792
792
  }
793
793
  h2 = h2 + 1 < n.length ? h2 + 1 : 0, w2 = w2 < n.length ? w2 + 0.125 : 0;
794
794
  }, r2);
@@ -5605,7 +5605,8 @@ async function registerDirectDataSource(opts) {
5605
5605
  console.log(
5606
5606
  `
5607
5607
  ${import_picocolors6.default.green("\u2713")} Registered${body2.host ? ` ${import_picocolors6.default.dim(body2.host)}` : ""}${body2.id ? ` ${import_picocolors6.default.dim(`(${body2.id})`)}` : ""} as a direct DataSource (${statusNote}).
5608
- Customer COMMIT is durable acceptance; correlated WAL promotes queued writes to confirmed.
5608
+ Your database is connected. Reads follow its replication stream; writes go through Ablo
5609
+ and land in your own tables. Check the connection anytime with ${import_picocolors6.default.cyan("ablo connect check")}.
5609
5610
  `
5610
5611
  );
5611
5612
  return true;
@@ -5840,17 +5841,134 @@ var init_disconnect = __esm({
5840
5841
  }
5841
5842
  });
5842
5843
 
5844
+ // src/cli/connectOwnership.ts
5845
+ function ownershipBlockers(rows) {
5846
+ return rows.filter((row) => !row.can_manage && !row.is_superuser).map((row) => ({
5847
+ relation: row.relation,
5848
+ owner: row.owner,
5849
+ canGrantInherit: row.can_grant_inherit
5850
+ }));
5851
+ }
5852
+ function ownershipRemediation(blockers, admin) {
5853
+ const grantableByOwner = /* @__PURE__ */ new Map();
5854
+ for (const blocker of blockers) {
5855
+ grantableByOwner.set(
5856
+ blocker.owner,
5857
+ (grantableByOwner.get(blocker.owner) ?? false) || blocker.canGrantInherit
5858
+ );
5859
+ }
5860
+ const resolvedOwners = /* @__PURE__ */ new Set();
5861
+ const inheritGrants = [];
5862
+ for (const [owner, grantable] of grantableByOwner) {
5863
+ if (grantable) {
5864
+ inheritGrants.push(`GRANT ${quoteIdent(owner)} TO ${quoteIdent(admin)} WITH INHERIT TRUE;`);
5865
+ resolvedOwners.add(owner);
5866
+ }
5867
+ }
5868
+ const unresolved = blockers.filter((blocker) => !resolvedOwners.has(blocker.owner));
5869
+ return { inheritGrants, unresolved };
5870
+ }
5871
+ async function publishedTableBlockers(sql, tables) {
5872
+ const scoped = tables.length > 0;
5873
+ const raw = await sql.unsafe(
5874
+ `SELECT format('%I.%I', n.nspname, c.relname) AS relation, ${OWNERSHIP_COLUMNS}
5875
+ ${OWNERSHIP_FROM}
5876
+ WHERE c.relkind = 'r'
5877
+ AND n.nspname = 'public'
5878
+ AND c.relname <> 'ablo_idempotency'
5879
+ ${scoped ? "AND c.relname = ANY($1)" : ""}`,
5880
+ scoped ? [tables] : []
5881
+ );
5882
+ return ownershipBlockers(import_zod7.z.array(ownedRelationRowSchema).parse(raw));
5883
+ }
5884
+ async function ledgerBlocker(sql) {
5885
+ const raw = await sql.unsafe(
5886
+ `SELECT format('%I.%I', n.nspname, c.relname) AS relation, ${OWNERSHIP_COLUMNS}
5887
+ ${OWNERSHIP_FROM}
5888
+ WHERE c.relkind = 'r'
5889
+ AND n.nspname = 'public'
5890
+ AND c.relname = 'ablo_idempotency'`
5891
+ );
5892
+ const rows = import_zod7.z.array(ownedRelationRowSchema).parse(raw);
5893
+ const row = rows[0];
5894
+ if (!row) return null;
5895
+ return ownershipBlockers([row])[0] ?? null;
5896
+ }
5897
+ function formatUnresolvedOwnership(unresolved, admin, target) {
5898
+ const list = unresolved.map((b4) => `${b4.relation} (owned by ${b4.owner})`).join("\n ");
5899
+ const owners = [...new Set(unresolved.map((b4) => b4.owner))];
5900
+ const ownerNames = owners.map((o2) => import_picocolors8.default.bold(o2)).join(", ");
5901
+ const grants = owners.map((o2) => `GRANT ${quoteIdent(o2)} TO ${quoteIdent(admin)} WITH INHERIT TRUE;`).join(" ");
5902
+ const one = unresolved.length === 1;
5903
+ const hasLedger = unresolved.some((b4) => b4.relation.endsWith(".ablo_idempotency"));
5904
+ return import_picocolors8.default.red(
5905
+ `
5906
+ ${import_picocolors8.default.bold(String(unresolved.length))} relation${one ? "" : "s"} on ${target} ${one ? "is" : "are"} owned by a role ${import_picocolors8.default.bold(admin)} can't manage or take over:`
5907
+ ) + `
5908
+ ${list}
5909
+
5910
+ Apply grants your admin inheritance of an owning role for you when it may, but ${admin}
5911
+ isn't a member with admin option of ${ownerNames}, so it can't here. Re-run ${import_picocolors8.default.bold("--url")} as a
5912
+ role that owns ${one ? "it" : "them"}, or have a role with admin option on ${ownerNames} (or a
5913
+ superuser) run:
5914
+ ${import_picocolors8.default.cyan(grants)}
5915
+ ` + (hasLedger ? `
5916
+ For ${import_picocolors8.default.bold("ablo_idempotency")} you can instead drop it \u2014 it holds only idempotency replay
5917
+ records, safe to drop when no commit is in flight \u2014 and Ablo recreates it under this admin:
5918
+ ${import_picocolors8.default.cyan("DROP TABLE ablo_idempotency;")}
5919
+ ` : "");
5920
+ }
5921
+ var import_zod7, import_picocolors8, ownedRelationRowSchema, OWNERSHIP_COLUMNS, OWNERSHIP_FROM;
5922
+ var init_connectOwnership = __esm({
5923
+ "src/cli/connectOwnership.ts"() {
5924
+ "use strict";
5925
+ init_cjs_shims();
5926
+ import_zod7 = require("zod");
5927
+ import_picocolors8 = __toESM(require_picocolors(), 1);
5928
+ init_connectSetup();
5929
+ ownedRelationRowSchema = import_zod7.z.object({
5930
+ /** Schema-qualified relation, e.g. `public.documents`. */
5931
+ relation: import_zod7.z.string(),
5932
+ owner: import_zod7.z.string(),
5933
+ /**
5934
+ * The admin can act as the owner for grants — it owns the relation OR is an
5935
+ * INHERITing member of the owning role (`pg_has_role(current_user, owner,
5936
+ * 'USAGE')`). A plain NOINHERIT membership is false: the plan doesn't
5937
+ * `SET ROLE`, so the grant would fail.
5938
+ */
5939
+ can_manage: import_zod7.z.boolean(),
5940
+ is_superuser: import_zod7.z.boolean(),
5941
+ /**
5942
+ * The admin is a member of the owning role WITH admin option, so it can run
5943
+ * `GRANT <owner> TO <admin> WITH INHERIT TRUE` itself to gain the inheritance
5944
+ * the grants need — the runnable fix reassigning ownership can't be.
5945
+ */
5946
+ can_grant_inherit: import_zod7.z.boolean()
5947
+ });
5948
+ OWNERSHIP_COLUMNS = `
5949
+ pg_get_userbyid(c.relowner) AS owner,
5950
+ pg_has_role(current_user, c.relowner, 'USAGE') AS can_manage,
5951
+ r.rolsuper AS is_superuser,
5952
+ EXISTS (
5953
+ SELECT 1 FROM pg_auth_members m
5954
+ WHERE m.member = r.oid AND m.roleid = c.relowner AND m.admin_option
5955
+ ) AS can_grant_inherit`;
5956
+ OWNERSHIP_FROM = `
5957
+ FROM pg_class c
5958
+ JOIN pg_namespace n ON n.oid = c.relnamespace
5959
+ JOIN pg_roles r ON r.rolname = current_user`;
5960
+ }
5961
+ });
5962
+
5843
5963
  // src/cli/connectApply.ts
5844
5964
  var connectApply_exports = {};
5845
5965
  __export(connectApply_exports, {
5846
5966
  connectApplyPlan: () => connectApplyPlan,
5847
5967
  detectProvider: () => detectProvider,
5848
- ledgerBlockedBy: () => ledgerBlockedBy,
5849
5968
  logicalReplicationGuidance: () => logicalReplicationGuidance,
5850
5969
  passwordClause: () => passwordClause,
5851
5970
  postRegistrationOutcome: () => postRegistrationOutcome,
5852
- runConnectApply: () => runConnectApply,
5853
- tableOwnershipBlockers: () => tableOwnershipBlockers
5971
+ runConnectApply: () => runConnectApply
5854
5972
  });
5855
5973
  function postRegistrationOutcome(input) {
5856
5974
  if (input.registered) return { exitCode: 0, notice: null };
@@ -5902,7 +6020,16 @@ EXCEPTION WHEN duplicate_object THEN NULL;
5902
6020
  END $$;`
5903
6021
  ];
5904
6022
  const publicationDetail = reconcile2?.sql.length === 0 ? "already publishing exactly these tables \u2014 nothing to change" : tables.length > 0 ? `a read stream of the ${tables.length} table${tables.length === 1 ? "" : "s"} you chose` : "a read stream of your tables";
6023
+ const ownStep = input.inheritGrants && input.inheritGrants.length > 0 ? [
6024
+ {
6025
+ key: "own",
6026
+ title: "Let this admin manage tables owned by another role",
6027
+ detail: "your admin inherits the owning role so the steps below apply \u2014 reversible, no ownership change",
6028
+ sql: input.inheritGrants
6029
+ }
6030
+ ] : [];
5905
6031
  return [
6032
+ ...ownStep,
5906
6033
  ...walStep,
5907
6034
  {
5908
6035
  key: "publication",
@@ -5944,15 +6071,15 @@ function printPlan2(steps, showSql) {
5944
6071
  console.log(` This sets up your database for Ablo:
5945
6072
  `);
5946
6073
  for (const step of steps) {
5947
- console.log(` ${import_picocolors8.default.green("\u2022")} ${step.title}`);
6074
+ console.log(` ${import_picocolors9.default.green("\u2022")} ${step.title}`);
5948
6075
  if (showSql) {
5949
6076
  for (const statement of step.sql) {
5950
- for (const line of statement.split("\n")) console.log(` ${import_picocolors8.default.dim(line)}`);
6077
+ for (const line of statement.split("\n")) console.log(` ${import_picocolors9.default.dim(line)}`);
5951
6078
  }
5952
6079
  }
5953
6080
  }
5954
6081
  console.log(
5955
- import_picocolors8.default.dim(
6082
+ import_picocolors9.default.dim(
5956
6083
  `
5957
6084
  Your admin password stays on this machine.${showSql ? "" : " (--show-sql for the exact statements)"}
5958
6085
  `
@@ -5965,45 +6092,6 @@ async function adminCanCreateRoles(sql) {
5965
6092
  );
5966
6093
  return rows[0] ?? null;
5967
6094
  }
5968
- function ledgerBlockedBy(row) {
5969
- if (!row || row.is_owner || row.is_superuser) return null;
5970
- return row.owner;
5971
- }
5972
- async function unmanageableLedgerOwner(sql) {
5973
- const rows = await sql.unsafe(
5974
- `SELECT pg_get_userbyid(c.relowner) AS owner,
5975
- c.relowner = r.oid AS is_owner,
5976
- r.rolsuper AS is_superuser
5977
- FROM pg_class c
5978
- JOIN pg_namespace n ON n.oid = c.relnamespace
5979
- JOIN pg_roles r ON r.rolname = current_user
5980
- WHERE c.relkind = 'r'
5981
- AND c.relname = 'ablo_idempotency'
5982
- AND n.nspname = 'public'`
5983
- );
5984
- return ledgerBlockedBy(rows[0]);
5985
- }
5986
- function tableOwnershipBlockers(rows) {
5987
- return rows.filter((row) => !row.can_manage && !row.is_superuser).map((row) => ({ relation: row.relation, owner: row.owner }));
5988
- }
5989
- async function unmanageablePublishedTableOwners(sql, tables) {
5990
- const scoped = tables.length > 0;
5991
- const rows = await sql.unsafe(
5992
- `SELECT format('%I.%I', n.nspname, c.relname) AS relation,
5993
- pg_get_userbyid(c.relowner) AS owner,
5994
- pg_has_role(current_user, c.relowner, 'USAGE') AS can_manage,
5995
- r.rolsuper AS is_superuser
5996
- FROM pg_class c
5997
- JOIN pg_namespace n ON n.oid = c.relnamespace
5998
- JOIN pg_roles r ON r.rolname = current_user
5999
- WHERE c.relkind = 'r'
6000
- AND n.nspname = 'public'
6001
- AND c.relname <> 'ablo_idempotency'
6002
- ${scoped ? "AND c.relname = ANY($1)" : ""}`,
6003
- scoped ? [tables] : []
6004
- );
6005
- return tableOwnershipBlockers(rows);
6006
- }
6007
6095
  function detectProvider(hostOrTarget) {
6008
6096
  const host = hostOrTarget.toLowerCase();
6009
6097
  if (host.includes("neon.tech")) return "neon";
@@ -6062,8 +6150,8 @@ async function runConnectApply(args) {
6062
6150
  const adminUrl = args.url ?? readProjectAdminDatabaseUrl();
6063
6151
  if (!adminUrl) {
6064
6152
  console.error(
6065
- import_picocolors8.default.red(" No admin connection string.") + import_picocolors8.default.dim(
6066
- ` Pass ${import_picocolors8.default.bold("--url <admin-conn>")} (or set ${import_picocolors8.default.bold("DATABASE_URL")}) and re-run.`
6153
+ import_picocolors9.default.red(" No admin connection string.") + import_picocolors9.default.dim(
6154
+ ` Pass ${import_picocolors9.default.bold("--url <admin-conn>")} (or set ${import_picocolors9.default.bold("DATABASE_URL")}) and re-run.`
6067
6155
  )
6068
6156
  );
6069
6157
  process.exit(1);
@@ -6078,19 +6166,19 @@ async function runConnectApply(args) {
6078
6166
  const apiKey = resolveApiKey();
6079
6167
  if (!apiKey) {
6080
6168
  console.error(
6081
- import_picocolors8.default.red(" Not logged in.") + import_picocolors8.default.dim(
6082
- ` Run ${import_picocolors8.default.bold("ablo login")} (or set ${import_picocolors8.default.bold("ABLO_API_KEY")}) so Ablo knows which project to register this database for.`
6169
+ import_picocolors9.default.red(" Not logged in.") + import_picocolors9.default.dim(
6170
+ ` Run ${import_picocolors9.default.bold("ablo login")} (or set ${import_picocolors9.default.bold("ABLO_API_KEY")}) so Ablo knows which project to register this database for.`
6083
6171
  )
6084
6172
  );
6085
6173
  process.exit(1);
6086
6174
  }
6087
6175
  console.log(
6088
6176
  `
6089
- ${brand("ablo")} ${import_picocolors8.default.dim(verb)} ${import_picocolors8.default.dim(rotating ? "re-key the scoped roles" : "set up your database for Ablo")}
6177
+ ${brand("ablo")} ${import_picocolors9.default.dim(verb)} ${import_picocolors9.default.dim(rotating ? "re-key the scoped roles" : "set up your database for Ablo")}
6090
6178
  `
6091
6179
  );
6092
6180
  console.log(
6093
- ` ${import_picocolors8.default.dim("\u2192")} ${import_picocolors8.default.bold(target)}${adminSource === "DATABASE_URL" ? import_picocolors8.default.dim(" (admin via DATABASE_URL)") : ""}
6181
+ ` ${import_picocolors9.default.dim("\u2192")} ${import_picocolors9.default.bold(target)}${adminSource === "DATABASE_URL" ? import_picocolors9.default.dim(" (admin via DATABASE_URL)") : ""}
6094
6182
  `
6095
6183
  );
6096
6184
  const admin = src_default(adminUrl, {
@@ -6106,53 +6194,25 @@ async function runConnectApply(args) {
6106
6194
  } catch (err) {
6107
6195
  await admin.end({ timeout: 2 }).catch(() => void 0);
6108
6196
  const pg = err ?? {};
6109
- console.error(import_picocolors8.default.red(` Couldn't connect: ${pg.message ?? String(err)}`));
6197
+ console.error(import_picocolors9.default.red(` Couldn't connect: ${pg.message ?? String(err)}`));
6110
6198
  process.exit(1);
6111
6199
  }
6112
6200
  if (!capability || !(capability.rolsuper || capability.rolcreaterole)) {
6113
6201
  await admin.end({ timeout: 2 });
6114
6202
  console.error(
6115
- import_picocolors8.default.red(` ${capability?.rolname ?? "This role"} can't create roles.`) + import_picocolors8.default.dim(` Point ${import_picocolors8.default.bold("--url")} at your owner/admin connection and re-run.`)
6203
+ import_picocolors9.default.red(` ${capability?.rolname ?? "This role"} can't create roles.`) + import_picocolors9.default.dim(` Point ${import_picocolors9.default.bold("--url")} at your owner/admin connection and re-run.`)
6116
6204
  );
6117
6205
  process.exit(1);
6118
6206
  }
6119
- const blockingOwner = await unmanageableLedgerOwner(admin).catch(() => null);
6120
- if (blockingOwner) {
6207
+ const ledger = await ledgerBlocker(admin).catch(() => null);
6208
+ const foreignTables = await publishedTableBlockers(admin, args.tables).catch(() => []);
6209
+ const { inheritGrants, unresolved } = ownershipRemediation(
6210
+ [...ledger ? [ledger] : [], ...foreignTables],
6211
+ capability.rolname
6212
+ );
6213
+ if (unresolved.length > 0) {
6121
6214
  await admin.end({ timeout: 2 });
6122
- console.error(
6123
- import_picocolors8.default.red(
6124
- `
6125
- ${import_picocolors8.default.bold("ablo_idempotency")} already exists on ${target}, owned by ${import_picocolors8.default.bold(blockingOwner)}, but you connected as ${import_picocolors8.default.bold(capability.rolname)}.`
6126
- ) + `
6127
- Ablo's setup grants the writer role access to this ledger, and Postgres reserves that
6128
- for the table's owner. Either re-run pointing ${import_picocolors8.default.bold("--url")} at ${import_picocolors8.default.bold(blockingOwner)}'s connection,
6129
- or drop the existing ledger so Ablo recreates it under this admin \u2014 it holds only
6130
- idempotency replay records, safe to drop when no commit is in flight:
6131
- ${import_picocolors8.default.cyan("DROP TABLE ablo_idempotency;")}
6132
- `
6133
- );
6134
- process.exit(1);
6135
- }
6136
- const foreignTables = await unmanageablePublishedTableOwners(admin, args.tables).catch(() => []);
6137
- if (foreignTables.length > 0) {
6138
- await admin.end({ timeout: 2 });
6139
- const list = foreignTables.map((t) => `${t.relation} (owned by ${t.owner})`).join("\n ");
6140
- const alters = foreignTables.map((t) => `ALTER TABLE ${t.relation} OWNER TO ${quoteIdent(capability.rolname)};`).join(" ");
6141
- const plural = foreignTables.length === 1;
6142
- console.error(
6143
- import_picocolors8.default.red(
6144
- `
6145
- ${import_picocolors8.default.bold(String(foreignTables.length))} published table${plural ? "" : "s"} ${plural ? "is" : "are"} owned by another role, but you connected as ${import_picocolors8.default.bold(capability.rolname)}:`
6146
- ) + `
6147
- ${list}
6148
-
6149
- Ablo grants the writer role access to your published tables, and Postgres reserves that
6150
- for the table's owner. Reassign them to your admin \u2014 metadata only, your rows and RLS
6151
- policies are untouched, and it works when your admin is a member of the owning role:
6152
- ${import_picocolors8.default.cyan(alters)}
6153
- Or re-run pointing ${import_picocolors8.default.bold("--url")} at the owning role's connection.
6154
- `
6155
- );
6215
+ console.error(formatUnresolvedOwnership(unresolved, capability.rolname, target));
6156
6216
  process.exit(1);
6157
6217
  }
6158
6218
  const provider = detectProvider(target);
@@ -6173,18 +6233,19 @@ async function runConnectApply(args) {
6173
6233
  },
6174
6234
  walAlreadyLogical: walReady,
6175
6235
  provider,
6176
- existingPublication
6236
+ existingPublication,
6237
+ inheritGrants
6177
6238
  });
6178
6239
  const steps = buildPlan("scram-verifier");
6179
6240
  if (pubReconcile.removed.length > 0 || pubReconcile.recreated) {
6180
6241
  console.log(
6181
- ` ${import_picocolors8.default.yellow("!")} ${import_picocolors8.default.bold(ABLO_PUBLICATION)} already publishes a different set; reconciling to your ${import_picocolors8.default.bold("--tables")}:`
6242
+ ` ${import_picocolors9.default.yellow("!")} ${import_picocolors9.default.bold(ABLO_PUBLICATION)} already publishes a different set; reconciling to your ${import_picocolors9.default.bold("--tables")}:`
6182
6243
  );
6183
- for (const t of pubReconcile.added) console.log(` ${import_picocolors8.default.green("+")} ${t}`);
6244
+ for (const t of pubReconcile.added) console.log(` ${import_picocolors9.default.green("+")} ${t}`);
6184
6245
  for (const t of pubReconcile.removed)
6185
- console.log(` ${import_picocolors8.default.red("-")} ${t} ${import_picocolors8.default.dim("(stops replicating to Ablo)")}`);
6246
+ console.log(` ${import_picocolors9.default.red("-")} ${t} ${import_picocolors9.default.dim("(stops replicating to Ablo)")}`);
6186
6247
  if (pubReconcile.recreated && existingPublication.allTables)
6187
- console.log(` ${import_picocolors8.default.red("-")} ${import_picocolors8.default.dim("every other table (was FOR ALL TABLES)")}`);
6248
+ console.log(` ${import_picocolors9.default.red("-")} ${import_picocolors9.default.dim("every other table (was FOR ALL TABLES)")}`);
6188
6249
  console.log();
6189
6250
  }
6190
6251
  printPlan2(steps, args.showSql);
@@ -6192,7 +6253,7 @@ async function runConnectApply(args) {
6192
6253
  if (!process.stdout.isTTY) {
6193
6254
  await admin.end({ timeout: 2 });
6194
6255
  console.error(
6195
- import_picocolors8.default.dim(` Re-run with ${import_picocolors8.default.bold("--yes")} to apply this in a non-interactive session.
6256
+ import_picocolors9.default.dim(` Re-run with ${import_picocolors9.default.bold("--yes")} to apply this in a non-interactive session.
6196
6257
  `)
6197
6258
  );
6198
6259
  process.exit(1);
@@ -6204,7 +6265,7 @@ async function runConnectApply(args) {
6204
6265
  if (pD(proceed) || !proceed) {
6205
6266
  await admin.end({ timeout: 2 });
6206
6267
  console.log(
6207
- import_picocolors8.default.dim(` Nothing applied. Run ${import_picocolors8.default.bold("ablo connect")} to see the manual recipe.
6268
+ import_picocolors9.default.dim(` Nothing applied. Run ${import_picocolors9.default.bold("ablo connect")} to see the manual recipe.
6208
6269
  `)
6209
6270
  );
6210
6271
  process.exit(0);
@@ -6216,8 +6277,8 @@ async function runConnectApply(args) {
6216
6277
  await admin.end({ timeout: 2 }).catch(() => void 0);
6217
6278
  const pg = err ?? {};
6218
6279
  console.error(
6219
- import_picocolors8.default.red(`
6220
- Setup stopped: ${pg.message ?? String(err)}`) + import_picocolors8.default.dim(` Every step is safe to re-run.
6280
+ import_picocolors9.default.red(`
6281
+ Setup stopped: ${pg.message ?? String(err)}`) + import_picocolors9.default.dim(` Every step is safe to re-run.
6221
6282
  `)
6222
6283
  );
6223
6284
  process.exit(1);
@@ -6228,14 +6289,14 @@ async function runConnectApply(args) {
6228
6289
  const replicationUrl = rewriteDatabaseUrl(adminUrl, role, replicationPassword);
6229
6290
  const writeUrl = rewriteDatabaseUrl(adminUrl, writeRole, writePassword);
6230
6291
  console.log(`
6231
- ${import_picocolors8.default.green("\u2713")} Roles ${rotating ? "re-keyed" : "created"}.
6292
+ ${import_picocolors9.default.green("\u2713")} Roles ${rotating ? "re-keyed" : "created"}.
6232
6293
  `);
6233
6294
  if (!walReady) {
6234
6295
  console.error(
6235
- ` ${import_picocolors8.default.yellow("!")} One step left \u2014 logical replication isn't on yet.
6296
+ ` ${import_picocolors9.default.yellow("!")} One step left \u2014 logical replication isn't on yet.
6236
6297
  ${logicalReplicationGuidance(provider)}.
6237
6298
 
6238
- Then re-run: ${import_picocolors8.default.cyan(`npx ablo ${verb}`)}
6299
+ Then re-run: ${import_picocolors9.default.cyan(`npx ablo ${verb}`)}
6239
6300
  `
6240
6301
  );
6241
6302
  process.exit(1);
@@ -6252,15 +6313,15 @@ async function runConnectApply(args) {
6252
6313
  items = await probeReadiness(verifier);
6253
6314
  } catch {
6254
6315
  items = null;
6255
- console.log(import_picocolors8.default.dim(` Couldn't verify from here; Ablo will validate from its own network.
6316
+ console.log(import_picocolors9.default.dim(` Couldn't verify from here; Ablo will validate from its own network.
6256
6317
  `));
6257
6318
  }
6258
6319
  await verifier.end({ timeout: 2 }).catch(() => void 0);
6259
6320
  const failed = items?.filter((i) => !i.ok) ?? [];
6260
6321
  if (failed.length > 0) {
6261
- for (const item of failed) console.log(` ${import_picocolors8.default.yellow("!")} ${item.label}`);
6322
+ for (const item of failed) console.log(` ${import_picocolors9.default.yellow("!")} ${item.label}`);
6262
6323
  console.log(`
6263
- ${import_picocolors8.default.dim("Resolve, then re-run")} ${import_picocolors8.default.cyan(`npx ablo ${verb}`)}
6324
+ ${import_picocolors9.default.dim("Resolve, then re-run")} ${import_picocolors9.default.cyan(`npx ablo ${verb}`)}
6264
6325
  `);
6265
6326
  process.exit(1);
6266
6327
  }
@@ -6275,20 +6336,21 @@ async function runConnectApply(args) {
6275
6336
  const outcome = postRegistrationOutcome({ rotating, registered });
6276
6337
  if (outcome.notice) {
6277
6338
  console.error(`
6278
- ${import_picocolors8.default.red(outcome.notice.split("\n").join("\n "))}
6339
+ ${import_picocolors9.default.red(outcome.notice.split("\n").join("\n "))}
6279
6340
  `);
6280
6341
  }
6281
6342
  process.exit(outcome.exitCode);
6282
6343
  }
6283
- var import_picocolors8;
6344
+ var import_picocolors9;
6284
6345
  var init_connectApply = __esm({
6285
6346
  "src/cli/connectApply.ts"() {
6286
6347
  "use strict";
6287
6348
  init_cjs_shims();
6288
- import_picocolors8 = __toESM(require_picocolors(), 1);
6349
+ import_picocolors9 = __toESM(require_picocolors(), 1);
6289
6350
  init_src();
6290
6351
  init_dist2();
6291
6352
  init_connectSetup();
6353
+ init_connectOwnership();
6292
6354
  init_dbRole();
6293
6355
  init_config();
6294
6356
  init_push();
@@ -218313,11 +218375,11 @@ var require_commonjs2 = __commonJS({
218313
218375
  if (pad) {
218314
218376
  const need = width - c.length;
218315
218377
  if (need > 0) {
218316
- const z8 = new Array(need + 1).join("0");
218378
+ const z9 = new Array(need + 1).join("0");
218317
218379
  if (i < 0) {
218318
- c = "-" + z8 + c.slice(1);
218380
+ c = "-" + z9 + c.slice(1);
218319
218381
  } else {
218320
- c = z8 + c;
218382
+ c = z9 + c;
218321
218383
  }
218322
218384
  }
218323
218385
  }
@@ -282246,7 +282308,7 @@ Node text: ${this.#forgottenText}`;
282246
282308
  // src/cli/index.ts
282247
282309
  init_cjs_shims();
282248
282310
  init_dist2();
282249
- var import_picocolors23 = __toESM(require_picocolors(), 1);
282311
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
282250
282312
  var import_fs12 = require("fs");
282251
282313
  var import_path7 = require("path");
282252
282314
  var import_child_process3 = require("child_process");
@@ -282437,7 +282499,7 @@ async function migrate(argv) {
282437
282499
  // src/cli/connect.ts
282438
282500
  init_cjs_shims();
282439
282501
  init_errors();
282440
- var import_picocolors9 = __toESM(require_picocolors(), 1);
282502
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
282441
282503
  init_src();
282442
282504
  init_dbRole();
282443
282505
  init_config();
@@ -282669,7 +282731,7 @@ function printConnectRecipe(args) {
282669
282731
  });
282670
282732
  console.log(
282671
282733
  `
282672
- ${brand("ablo")} ${import_picocolors9.default.dim("connect")} ${import_picocolors9.default.dim("direct writes + WAL-settled sync")}
282734
+ ${brand("ablo")} ${import_picocolors10.default.dim("connect")} ${import_picocolors10.default.dim("direct writes + WAL-settled sync")}
282673
282735
  `
282674
282736
  );
282675
282737
  console.log(
@@ -282677,29 +282739,29 @@ function printConnectRecipe(args) {
282677
282739
  observes what committed, orders it with external writes, and confirms it in the sync log.
282678
282740
  Your database stays authoritative; Ablo never owns or migrates your application tables.
282679
282741
 
282680
- ${import_picocolors9.default.bold("ablo connect apply")} runs every step below for you from a one-time admin
282681
- connection and leaves your app holding only ${import_picocolors9.default.bold("ABLO_API_KEY")}. To do it by hand,
282682
- run this once against your Postgres ${import_picocolors9.default.dim("(as a superuser / DB owner)")}:
282742
+ ${import_picocolors10.default.bold("ablo connect apply")} runs every step below for you from a one-time admin
282743
+ connection and leaves your app holding only ${import_picocolors10.default.bold("ABLO_API_KEY")}. To do it by hand,
282744
+ run this once against your Postgres ${import_picocolors10.default.dim("(as a superuser / DB owner)")}:
282683
282745
  `
282684
282746
  );
282685
282747
  console.log(
282686
- ` ${import_picocolors9.default.bold("1.")} Enable logical decoding ${import_picocolors9.default.dim("(then RESTART Postgres \u2014 wal_level is not reloadable)")}`
282748
+ ` ${import_picocolors10.default.bold("1.")} Enable logical decoding ${import_picocolors10.default.dim("(then RESTART Postgres \u2014 wal_level is not reloadable)")}`
282687
282749
  );
282688
- console.log(` ${import_picocolors9.default.cyan(sql[0])}`);
282750
+ console.log(` ${import_picocolors10.default.cyan(sql[0])}`);
282689
282751
  console.log(
282690
- import_picocolors9.default.dim(
282691
- ` Managed hosts don't take ALTER SYSTEM: on Amazon RDS / Aurora set ${import_picocolors9.default.bold("rds.logical_replication = 1")}
282752
+ import_picocolors10.default.dim(
282753
+ ` Managed hosts don't take ALTER SYSTEM: on Amazon RDS / Aurora set ${import_picocolors10.default.bold("rds.logical_replication = 1")}
282692
282754
  in the parameter group and reboot; on Neon or Supabase enable logical replication in the
282693
- project settings. ${import_picocolors9.default.bold("ablo connect apply")} detects the host and does the right thing.`
282755
+ project settings. ${import_picocolors10.default.bold("ablo connect apply")} detects the host and does the right thing.`
282694
282756
  )
282695
282757
  );
282696
282758
  console.log(`
282697
- ${import_picocolors9.default.bold("2.")} Publish the tables Ablo should read`);
282698
- console.log(` ${import_picocolors9.default.cyan(sql[1])}`);
282759
+ ${import_picocolors10.default.bold("2.")} Publish the tables Ablo should read`);
282760
+ console.log(` ${import_picocolors10.default.cyan(sql[1])}`);
282699
282761
  if (args.tables.length === 0) {
282700
282762
  console.log(
282701
- import_picocolors9.default.dim(
282702
- ` (Scope it with ${import_picocolors9.default.bold("ablo connect --tables a,b,c")} to publish a subset.)`
282763
+ import_picocolors10.default.dim(
282764
+ ` (Scope it with ${import_picocolors10.default.bold("ablo connect --tables a,b,c")} to publish a subset.)`
282703
282765
  )
282704
282766
  );
282705
282767
  }
@@ -282708,52 +282770,52 @@ function printConnectRecipe(args) {
282708
282770
  const writerStatements = sql.slice(writerStart);
282709
282771
  console.log(
282710
282772
  `
282711
- ${import_picocolors9.default.bold("3.")} Create the scoped replication role ${import_picocolors9.default.dim("(pick your own replication password)")}`
282773
+ ${import_picocolors10.default.bold("3.")} Create the scoped replication role ${import_picocolors10.default.dim("(pick your own replication password)")}`
282712
282774
  );
282713
282775
  for (const statement of replicationStatements) {
282714
- for (const line of statement.split("\n")) console.log(` ${import_picocolors9.default.cyan(line)}`);
282776
+ for (const line of statement.split("\n")) console.log(` ${import_picocolors10.default.cyan(line)}`);
282715
282777
  }
282716
282778
  console.log(
282717
- import_picocolors9.default.dim(
282779
+ import_picocolors10.default.dim(
282718
282780
  ` On Amazon RDS, the REPLICATION attribute is granted, not set directly:
282719
- ${import_picocolors9.default.bold(`GRANT rds_replication TO ${quoteIdent(args.role)};`)}`
282781
+ ${import_picocolors10.default.bold(`GRANT rds_replication TO ${quoteIdent(args.role)};`)}`
282720
282782
  )
282721
282783
  );
282722
282784
  console.log(
282723
282785
  `
282724
- ${import_picocolors9.default.bold("4.")} Create the separate DML role and the idempotency ledger ` + import_picocolors9.default.dim("(pick your own write password)")
282786
+ ${import_picocolors10.default.bold("4.")} Create the separate DML role and the idempotency ledger ` + import_picocolors10.default.dim("(pick your own write password)")
282725
282787
  );
282726
282788
  for (const statement of writerStatements) {
282727
- for (const line of statement.split("\n")) console.log(` ${import_picocolors9.default.cyan(line)}`);
282789
+ for (const line of statement.split("\n")) console.log(` ${import_picocolors10.default.cyan(line)}`);
282728
282790
  }
282729
282791
  console.log(
282730
- import_picocolors9.default.dim(
282792
+ import_picocolors10.default.dim(
282731
282793
  ` The writer gets row DML + ledger access only. It has no REPLICATION, schema CREATE,
282732
282794
  role administration, database creation, ownership, or customer-table DDL. Direct uses
282733
- ${import_picocolors9.default.bold("ablo_idempotency")} but no outbox; WAL carries the committed row changes.
282734
- Each ledger row carries an ${import_picocolors9.default.bold("expires_at")}; the writer can't DELETE (tamper-
282795
+ ${import_picocolors10.default.bold("ablo_idempotency")} but no outbox; WAL carries the committed row changes.
282796
+ Each ledger row carries an ${import_picocolors10.default.bold("expires_at")}; the writer can't DELETE (tamper-
282735
282797
  resistance), so prune it from your own admin/cron when convenient:
282736
- ${import_picocolors9.default.bold("DELETE FROM ablo_idempotency WHERE expires_at < now();")}`
282798
+ ${import_picocolors10.default.bold("DELETE FROM ablo_idempotency WHERE expires_at < now();")}`
282737
282799
  )
282738
282800
  );
282739
282801
  console.log(
282740
282802
  `
282741
- ${import_picocolors9.default.bold("5.")} Register the two roles with Ablo. Set them just long enough to register \u2014
282742
- Ablo holds them from here, so your app keeps only ${import_picocolors9.default.bold("ABLO_API_KEY")}:
282743
- ${import_picocolors9.default.bold("ABLO_REPLICATION_DATABASE_URL")} ${import_picocolors9.default.dim(`\u2192 ${args.role} (replication only)`)}
282744
- ${import_picocolors9.default.bold("ABLO_WRITE_DATABASE_URL")} ${import_picocolors9.default.dim(`\u2192 ${args.writeRole} (DML only)`)}
282745
- ${import_picocolors9.default.cyan("npx ablo connect register")}
282803
+ ${import_picocolors10.default.bold("5.")} Register the two roles with Ablo. Set them just long enough to register \u2014
282804
+ Ablo holds them from here, so your app keeps only ${import_picocolors10.default.bold("ABLO_API_KEY")}:
282805
+ ${import_picocolors10.default.bold("ABLO_REPLICATION_DATABASE_URL")} ${import_picocolors10.default.dim(`\u2192 ${args.role} (replication only)`)}
282806
+ ${import_picocolors10.default.bold("ABLO_WRITE_DATABASE_URL")} ${import_picocolors10.default.dim(`\u2192 ${args.writeRole} (DML only)`)}
282807
+ ${import_picocolors10.default.cyan("npx ablo connect register")}
282746
282808
  `
282747
282809
  );
282748
282810
  console.log(
282749
- ` ${import_picocolors9.default.bold("6.")} Verify readiness ${import_picocolors9.default.dim("(checked from Ablo\u2019s side \u2014 needs only ABLO_API_KEY):")}
282750
- ${import_picocolors9.default.cyan("npx ablo connect check")}
282811
+ ` ${import_picocolors10.default.bold("6.")} Verify readiness ${import_picocolors10.default.dim("(checked from Ablo\u2019s side \u2014 needs only ABLO_API_KEY):")}
282812
+ ${import_picocolors10.default.cyan("npx ablo connect check")}
282751
282813
  `
282752
282814
  );
282753
282815
  console.log(
282754
- import_picocolors9.default.dim(
282816
+ import_picocolors10.default.dim(
282755
282817
  ` Reachable databases use this direct path. If no inbound route can be established, use the
282756
- signed ${import_picocolors9.default.bold("dataSource()")} endpoint fallback; its correlated events confirm writes
282818
+ signed ${import_picocolors10.default.bold("dataSource()")} endpoint fallback; its correlated events confirm writes
282757
282819
  without an Ablo-side customer database socket.`
282758
282820
  )
282759
282821
  );
@@ -282768,11 +282830,11 @@ var SYNC_INFRA_RELATIONS = [
282768
282830
  var SYNC_INFRA_TYPES = ["participant_kind", "backfill_provenance", "confirmation_state"];
282769
282831
  function printCheckItem(item) {
282770
282832
  if (item.ok) {
282771
- console.log(` ${import_picocolors9.default.green("\u2713")} ${item.label}`);
282833
+ console.log(` ${import_picocolors10.default.green("\u2713")} ${item.label}`);
282772
282834
  } else {
282773
- console.log(` ${import_picocolors9.default.red("\u2717")} ${item.label}`);
282835
+ console.log(` ${import_picocolors10.default.red("\u2717")} ${item.label}`);
282774
282836
  if (item.fix) {
282775
- for (const line of item.fix.split("\n")) console.log(` ${import_picocolors9.default.red("\u2022")} ${line}`);
282837
+ for (const line of item.fix.split("\n")) console.log(` ${import_picocolors10.default.red("\u2022")} ${line}`);
282776
282838
  }
282777
282839
  }
282778
282840
  }
@@ -282793,7 +282855,7 @@ async function probeDirectWriteReadiness(sql, opts = {}) {
282793
282855
  ok: false,
282794
282856
  label: `ABLO_WRITE_DATABASE_URL is not a scoped DML role`,
282795
282857
  fix: `Use ${ABLO_WRITE_ROLE} from the setup recipe: NOSUPERUSER, NOBYPASSRLS, NOCREATEDB, NOCREATEROLE, NOREPLICATION.`
282796
- } : { ok: true, label: `write role ${import_picocolors9.default.bold(role?.rolname ?? ABLO_WRITE_ROLE)} is DML-only` }
282858
+ } : { ok: true, label: `write role ${import_picocolors10.default.bold(role?.rolname ?? ABLO_WRITE_ROLE)} is DML-only` }
282797
282859
  );
282798
282860
  const schemaRows = await sql.unsafe(
282799
282861
  `SELECT
@@ -282820,10 +282882,10 @@ async function probeDirectWriteReadiness(sql, opts = {}) {
282820
282882
  );
282821
282883
  const ledger = ledgerRows[0];
282822
282884
  items.push(
282823
- ledger?.present && ledger.writes && !ledger.deletes ? { ok: true, label: `${import_picocolors9.default.bold("ablo_idempotency")} is durable and protected from DELETE` } : {
282885
+ ledger?.present && ledger.writes && !ledger.deletes ? { ok: true, label: `${import_picocolors10.default.bold("ablo_idempotency")} is durable and protected from DELETE` } : {
282824
282886
  ok: false,
282825
- label: `${import_picocolors9.default.bold("ablo_idempotency")} is missing or has unsafe grants`,
282826
- fix: `Apply the DML-role and idempotency-ledger statements printed by ${import_picocolors9.default.bold("ablo connect")}.`
282887
+ label: `${import_picocolors10.default.bold("ablo_idempotency")} is missing or has unsafe grants`,
282888
+ fix: `Apply the DML-role and idempotency-ledger statements printed by ${import_picocolors10.default.bold("ablo connect")}.`
282827
282889
  }
282828
282890
  );
282829
282891
  const tableRows = await sql.unsafe(
@@ -282912,8 +282974,8 @@ function requireScopedUrl(kind, verb) {
282912
282974
  const resolved = readProjectReplicationUrlWithSource();
282913
282975
  if (resolved) return resolved.url;
282914
282976
  console.error(
282915
- import_picocolors9.default.red(" No replication connection found (checked process env, .env.local, .env).") + import_picocolors9.default.dim(
282916
- ` Set ${import_picocolors9.default.bold("ABLO_REPLICATION_DATABASE_URL")} to the ${ABLO_REPLICATION_ROLE} connection printed by ${import_picocolors9.default.bold("ablo connect")}, then re-run ${import_picocolors9.default.bold(`ablo connect ${verb}`)}.`
282977
+ import_picocolors10.default.red(" No replication connection found (checked process env, .env.local, .env).") + import_picocolors10.default.dim(
282978
+ ` Set ${import_picocolors10.default.bold("ABLO_REPLICATION_DATABASE_URL")} to the ${ABLO_REPLICATION_ROLE} connection printed by ${import_picocolors10.default.bold("ablo connect")}, then re-run ${import_picocolors10.default.bold(`ablo connect ${verb}`)}.`
282917
282979
  )
282918
282980
  );
282919
282981
  process.exit(1);
@@ -282921,8 +282983,8 @@ function requireScopedUrl(kind, verb) {
282921
282983
  const dbUrl = readProjectWriteDatabaseUrl();
282922
282984
  if (dbUrl) return dbUrl;
282923
282985
  console.error(
282924
- import_picocolors9.default.red(" No ABLO_WRITE_DATABASE_URL found (checked process env, .env.local, .env).") + import_picocolors9.default.dim(
282925
- ` Set it to the ${ABLO_WRITE_ROLE} connection printed by ${import_picocolors9.default.bold("ablo connect")}, then re-run ${import_picocolors9.default.bold(`ablo connect ${verb}`)}. The replication credential is never reused for DML.`
282986
+ import_picocolors10.default.red(" No ABLO_WRITE_DATABASE_URL found (checked process env, .env.local, .env).") + import_picocolors10.default.dim(
282987
+ ` Set it to the ${ABLO_WRITE_ROLE} connection printed by ${import_picocolors10.default.bold("ablo connect")}, then re-run ${import_picocolors10.default.bold(`ablo connect ${verb}`)}. The replication credential is never reused for DML.`
282926
282988
  )
282927
282989
  );
282928
282990
  process.exit(1);
@@ -282938,7 +283000,7 @@ async function probeAndReport(dbUrl, kind = "replication") {
282938
283000
  const dial = dialFailureReason(err);
282939
283001
  if (dial) return { kind: "no-dial", reason: dial };
282940
283002
  const pg = err ?? {};
282941
- console.error(import_picocolors9.default.red(` Couldn't read the database: ${pg.message ?? String(err)}`));
283003
+ console.error(import_picocolors10.default.red(` Couldn't read the database: ${pg.message ?? String(err)}`));
282942
283004
  process.exit(1);
282943
283005
  }
282944
283006
  await sql.end({ timeout: 2 });
@@ -282948,14 +283010,14 @@ async function probeAndReport(dbUrl, kind = "replication") {
282948
283010
  async function runCheck() {
282949
283011
  console.log(
282950
283012
  `
282951
- ${brand("ablo")} ${import_picocolors9.default.dim("connect check")} ${import_picocolors9.default.dim("direct-write + WAL readiness")}
283013
+ ${brand("ablo")} ${import_picocolors10.default.dim("connect check")} ${import_picocolors10.default.dim("direct-write + WAL readiness")}
282952
283014
  `
282953
283015
  );
282954
283016
  const apiKey = resolveApiKey();
282955
283017
  if (!apiKey) {
282956
283018
  console.error(
282957
- import_picocolors9.default.red(" No API key found.") + import_picocolors9.default.dim(
282958
- ` Run ${import_picocolors9.default.bold("ablo login")} (or set ${import_picocolors9.default.bold("ABLO_API_KEY")}), then re-run ${import_picocolors9.default.bold("ablo connect check")}.`
283019
+ import_picocolors10.default.red(" No API key found.") + import_picocolors10.default.dim(
283020
+ ` Run ${import_picocolors10.default.bold("ablo login")} (or set ${import_picocolors10.default.bold("ABLO_API_KEY")}), then re-run ${import_picocolors10.default.bold("ablo connect check")}.`
282959
283021
  )
282960
283022
  );
282961
283023
  process.exit(1);
@@ -282965,19 +283027,19 @@ async function runCheck() {
282965
283027
  if (!result.ok) {
282966
283028
  if (result.code === "no_data_source_registered") {
282967
283029
  console.error(
282968
- ` ${import_picocolors9.default.yellow("\u2014")} No database is connected to this plane yet, so there's nothing to check.
282969
- ` + import_picocolors9.default.dim(
282970
- ` Connect one with ${import_picocolors9.default.bold("ablo connect apply")}, then re-run ${import_picocolors9.default.bold("ablo connect check")}.
283030
+ ` ${import_picocolors10.default.yellow("\u2014")} No database is connected to this plane yet, so there's nothing to check.
283031
+ ` + import_picocolors10.default.dim(
283032
+ ` Connect one with ${import_picocolors10.default.bold("ablo connect apply")}, then re-run ${import_picocolors10.default.bold("ablo connect check")}.
282971
283033
  `
282972
283034
  )
282973
283035
  );
282974
283036
  process.exit(1);
282975
283037
  }
282976
- console.error(import_picocolors9.default.red(` The check failed: ${result.message}`));
283038
+ console.error(import_picocolors10.default.red(` The check failed: ${result.message}`));
282977
283039
  if (result.code === "forbidden") {
282978
283040
  console.error(
282979
- import_picocolors9.default.dim(
282980
- ` Checking a connected database needs a ${import_picocolors9.default.bold("secret")} key (sk_\u2026). Run ${import_picocolors9.default.bold("ablo login")} for one.`
283041
+ import_picocolors10.default.dim(
283042
+ ` Checking a connected database needs a ${import_picocolors10.default.bold("secret")} key (sk_\u2026). Run ${import_picocolors10.default.bold("ablo login")} for one.`
282981
283043
  )
282982
283044
  );
282983
283045
  }
@@ -282986,12 +283048,12 @@ async function runCheck() {
282986
283048
  }
282987
283049
  if (!result.reachable) {
282988
283050
  console.error(
282989
- ` ${import_picocolors9.default.red("\u2717")} Ablo's infrastructure can't reach your database${result.reason ? ` ${import_picocolors9.default.dim(`(${result.reason})`)}` : ""}.`
283051
+ ` ${import_picocolors10.default.red("\u2717")} Ablo's infrastructure can't reach your database${result.reason ? ` ${import_picocolors10.default.dim(`(${result.reason})`)}` : ""}.`
282990
283052
  );
282991
283053
  console.error(
282992
- import_picocolors9.default.dim(
283054
+ import_picocolors10.default.dim(
282993
283055
  ` Direct needs a route Ablo's servers can dial \u2014 public allowlist, PrivateLink, peering,
282994
- or VPN. Only when no inbound route can exist, use the signed ${import_picocolors9.default.bold("dataSource()")} endpoint fallback.
283056
+ or VPN. Only when no inbound route can exist, use the signed ${import_picocolors10.default.bold("dataSource()")} endpoint fallback.
282995
283057
  `
282996
283058
  )
282997
283059
  );
@@ -283004,14 +283066,14 @@ async function runCheck() {
283004
283066
  console.log();
283005
283067
  if (result.ready) {
283006
283068
  console.log(
283007
- ` ${import_picocolors9.default.green("\u2713")} Ready \u2014 checked from Ablo's infrastructure. Ablo can apply scoped DML and settle it from WAL.
283069
+ ` ${import_picocolors10.default.green("\u2713")} Ready \u2014 checked from Ablo's infrastructure. Ablo can apply scoped DML and settle it from WAL.
283008
283070
  `
283009
283071
  );
283010
283072
  process.exit(0);
283011
283073
  }
283012
283074
  const count = result.failures.length;
283013
283075
  console.log(
283014
- ` ${import_picocolors9.default.red(`${count} item${count === 1 ? "" : "s"} to fix`)} ${import_picocolors9.default.dim(`\u2014 apply the fixes above, then re-run ${import_picocolors9.default.bold("ablo connect check")}.`)}
283076
+ ` ${import_picocolors10.default.red(`${count} item${count === 1 ? "" : "s"} to fix`)} ${import_picocolors10.default.dim(`\u2014 apply the fixes above, then re-run ${import_picocolors10.default.bold("ablo connect check")}.`)}
283015
283077
  `
283016
283078
  );
283017
283079
  process.exit(1);
@@ -283022,22 +283084,22 @@ async function runRegister(args) {
283022
283084
  const apiKey = resolveApiKey();
283023
283085
  if (!apiKey) {
283024
283086
  console.error(
283025
- import_picocolors9.default.red(" Not logged in.") + import_picocolors9.default.dim(
283026
- ` Run ${import_picocolors9.default.bold("ablo login")} (or set ${import_picocolors9.default.bold("ABLO_API_KEY")}) so Ablo knows which project to register this database for.`
283087
+ import_picocolors10.default.red(" Not logged in.") + import_picocolors10.default.dim(
283088
+ ` Run ${import_picocolors10.default.bold("ablo login")} (or set ${import_picocolors10.default.bold("ABLO_API_KEY")}) so Ablo knows which project to register this database for.`
283027
283089
  )
283028
283090
  );
283029
283091
  process.exit(1);
283030
283092
  }
283031
283093
  console.log(
283032
283094
  `
283033
- ${brand("ablo")} ${import_picocolors9.default.dim("connect register")} ${import_picocolors9.default.dim("register a direct DataSource")}
283095
+ ${brand("ablo")} ${import_picocolors10.default.dim("connect register")} ${import_picocolors10.default.dim("register a direct DataSource")}
283034
283096
  `
283035
283097
  );
283036
- console.log(` ${import_picocolors9.default.bold("Replication role")}
283098
+ console.log(` ${import_picocolors10.default.bold("Replication role")}
283037
283099
  `);
283038
283100
  const replication = await probeAndReport(dbUrl, "replication");
283039
283101
  console.log(`
283040
- ${import_picocolors9.default.bold("Direct-write role")}
283102
+ ${import_picocolors10.default.bold("Direct-write role")}
283041
283103
  `);
283042
283104
  const write = await probeAndReport(writeDbUrl, "write");
283043
283105
  const noDial = [
@@ -283047,7 +283109,7 @@ async function runRegister(args) {
283047
283109
  const failures = (replication.kind === "probed" ? replication.failures : 0) + (write.kind === "probed" ? write.failures : 0);
283048
283110
  if (noDial.length > 0) {
283049
283111
  console.log(
283050
- ` This machine can't reach one or both scoped connections (${import_picocolors9.default.dim(noDial.join("; "))}) \u2014 continuing anyway.
283112
+ ` This machine can't reach one or both scoped connections (${import_picocolors10.default.dim(noDial.join("; "))}) \u2014 continuing anyway.
283051
283113
  Ablo validates both credentials from the infrastructure that will use them and refuses
283052
283114
  registration unless replication and direct DML are both ready.
283053
283115
  `
@@ -283056,7 +283118,7 @@ async function runRegister(args) {
283056
283118
  if (failures > 0) {
283057
283119
  console.log(
283058
283120
  `
283059
- ${import_picocolors9.default.red(`${failures} item${failures === 1 ? "" : "s"} to fix`)} ${import_picocolors9.default.dim("\u2014 direct registration requires both scoped roles. Fix the above, then re-run.")}
283121
+ ${import_picocolors10.default.red(`${failures} item${failures === 1 ? "" : "s"} to fix`)} ${import_picocolors10.default.dim("\u2014 direct registration requires both scoped roles. Fix the above, then re-run.")}
283060
283122
  `
283061
283123
  );
283062
283124
  process.exit(1);
@@ -283080,31 +283142,31 @@ async function runScan() {
283080
283142
  artifacts = await auditTenantSyncInfra(sql);
283081
283143
  } catch (err) {
283082
283144
  const pg = err ?? {};
283083
- console.error(import_picocolors9.default.red(` Couldn't audit the database: ${pg.message ?? String(err)}`));
283145
+ console.error(import_picocolors10.default.red(` Couldn't audit the database: ${pg.message ?? String(err)}`));
283084
283146
  await sql.end({ timeout: 2 });
283085
283147
  process.exit(1);
283086
283148
  }
283087
283149
  await sql.end({ timeout: 2 });
283088
283150
  console.log(
283089
283151
  `
283090
- ${brand("ablo")} ${import_picocolors9.default.dim("connect scan")} ${import_picocolors9.default.dim("audit for leftover Ablo sync infrastructure")}
283152
+ ${brand("ablo")} ${import_picocolors10.default.dim("connect scan")} ${import_picocolors10.default.dim("audit for leftover Ablo sync infrastructure")}
283091
283153
  `
283092
283154
  );
283093
283155
  const present = artifacts.filter((a) => a.present);
283094
283156
  if (present.length === 0) {
283095
- console.log(` ${import_picocolors9.default.green("\u2713")} No deprecated Ablo sync infrastructure found in public.
283157
+ console.log(` ${import_picocolors10.default.green("\u2713")} No deprecated Ablo sync infrastructure found in public.
283096
283158
  `);
283097
283159
  process.exit(0);
283098
283160
  }
283099
283161
  for (const artifact of present) {
283100
283162
  const label = artifact.kind === "type" ? "type" : "relation";
283101
- console.log(` ${import_picocolors9.default.yellow("!")} ${label} ${import_picocolors9.default.bold(`public.${artifact.name}`)} exists`);
283163
+ console.log(` ${import_picocolors10.default.yellow("!")} ${label} ${import_picocolors10.default.bold(`public.${artifact.name}`)} exists`);
283102
283164
  }
283103
283165
  console.log(
283104
283166
  `
283105
- ${import_picocolors9.default.yellow(`${present.length} artifact${present.length === 1 ? "" : "s"} found`)} ` + import_picocolors9.default.dim(
283167
+ ${import_picocolors10.default.yellow(`${present.length} artifact${present.length === 1 ? "" : "s"} found`)} ` + import_picocolors10.default.dim(
283106
283168
  "\u2014 do not drop automatically. Confirm the org/environment is log-authoritative, then follow "
283107
- ) + import_picocolors9.default.bold("docs/runbooks/wal-stage5-customer-db-infra-cleanup.md") + import_picocolors9.default.dim(".\n")
283169
+ ) + import_picocolors10.default.bold("docs/runbooks/wal-stage5-customer-db-infra-cleanup.md") + import_picocolors10.default.dim(".\n")
283108
283170
  );
283109
283171
  process.exit(1);
283110
283172
  }
@@ -283118,7 +283180,7 @@ async function connect(argv) {
283118
283180
  try {
283119
283181
  args = parseConnectArgs(argv);
283120
283182
  } catch (err) {
283121
- console.error(import_picocolors9.default.red(` ${err instanceof Error ? err.message : String(err)}`));
283183
+ console.error(import_picocolors10.default.red(` ${err instanceof Error ? err.message : String(err)}`));
283122
283184
  process.exit(1);
283123
283185
  }
283124
283186
  if (args.apply || args.rotate) {
@@ -283174,7 +283236,7 @@ init_cjs_shims();
283174
283236
  init_errors();
283175
283237
  var import_fs6 = require("fs");
283176
283238
  var import_path4 = require("path");
283177
- var import_picocolors10 = __toESM(require_picocolors(), 1);
283239
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
283178
283240
  var import_schema4 = require("@abloatai/ablo/schema");
283179
283241
  init_push();
283180
283242
  var DEFAULT_SCHEMA_PATH3 = "ablo/schema.ts";
@@ -283207,7 +283269,7 @@ async function generate(argv) {
283207
283269
  try {
283208
283270
  args = parseGenerateArgs(argv);
283209
283271
  } catch (err) {
283210
- console.error(import_picocolors10.default.red(` ${err instanceof Error ? err.message : String(err)}`));
283272
+ console.error(import_picocolors11.default.red(` ${err instanceof Error ? err.message : String(err)}`));
283211
283273
  process.exit(1);
283212
283274
  }
283213
283275
  let source;
@@ -283216,20 +283278,20 @@ async function generate(argv) {
283216
283278
  const schemaJson = JSON.parse((0, import_schema4.serializeSchema)(schema));
283217
283279
  source = (0, import_schema4.generateTypes)(schemaJson);
283218
283280
  } catch (err) {
283219
- console.error(import_picocolors10.default.red(` ${err instanceof Error ? err.message : String(err)}`));
283281
+ console.error(import_picocolors11.default.red(` ${err instanceof Error ? err.message : String(err)}`));
283220
283282
  process.exit(1);
283221
283283
  }
283222
283284
  const abs = (0, import_path4.resolve)(process.cwd(), args.out);
283223
283285
  (0, import_fs6.mkdirSync)((0, import_path4.dirname)(abs), { recursive: true });
283224
283286
  (0, import_fs6.writeFileSync)(abs, source);
283225
- console.log(` ${import_picocolors10.default.green("\u2713")} Generated types \u2192 ${import_picocolors10.default.bold(args.out)}`);
283287
+ console.log(` ${import_picocolors11.default.green("\u2713")} Generated types \u2192 ${import_picocolors11.default.bold(args.out)}`);
283226
283288
  }
283227
283289
 
283228
283290
  // src/cli/dev.ts
283229
283291
  init_cjs_shims();
283230
283292
  init_errors();
283231
283293
  init_credentialPolicy();
283232
- var import_picocolors11 = __toESM(require_picocolors(), 1);
283294
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
283233
283295
  init_dist2();
283234
283296
  var import_fs7 = require("fs");
283235
283297
  var import_path5 = require("path");
@@ -283271,25 +283333,25 @@ function classifyKey(apiKey, activeMode) {
283271
283333
  if (!apiKey) {
283272
283334
  return {
283273
283335
  ok: false,
283274
- reason: `No API key. Run ${import_picocolors11.default.bold("npx ablo login")} for the sandbox dev loop \u2014 or set ${import_picocolors11.default.bold("ABLO_API_KEY")} (${import_picocolors11.default.bold("sk_test_")} = sandbox; ${import_picocolors11.default.bold("sk_live_")} = deliberate production deploy). ` + import_picocolors11.default.dim(`Mode is currently '${activeMode}'.`)
283336
+ reason: `No API key. Run ${import_picocolors12.default.bold("npx ablo login")} for the sandbox dev loop \u2014 or set ${import_picocolors12.default.bold("ABLO_API_KEY")} (${import_picocolors12.default.bold("sk_test_")} = sandbox; ${import_picocolors12.default.bold("sk_live_")} = deliberate production deploy). ` + import_picocolors12.default.dim(`Mode is currently '${activeMode}'.`)
283275
283337
  };
283276
283338
  }
283277
283339
  if (apiKey.startsWith("sk_test_")) return { ok: true };
283278
283340
  if (apiKey.startsWith("sk_live_")) {
283279
283341
  return {
283280
283342
  ok: false,
283281
- reason: `Production schema deploys run one-shot: ${import_picocolors11.default.bold("ABLO_API_KEY=sk_live_\u2026 npx ablo push")} (or ${import_picocolors11.default.bold("ablo mode production")}). ${import_picocolors11.default.bold("--watch")} is sandbox-only.`
283343
+ reason: `Production schema deploys run one-shot: ${import_picocolors12.default.bold("ABLO_API_KEY=sk_live_\u2026 npx ablo push")} (or ${import_picocolors12.default.bold("ablo mode production")}). ${import_picocolors12.default.bold("--watch")} is sandbox-only.`
283282
283344
  };
283283
283345
  }
283284
283346
  if (classifyCredentialKind(apiKey) === "restricted") {
283285
283347
  return {
283286
283348
  ok: false,
283287
- reason: `Restricted (${import_picocolors11.default.bold("rk_")}) keys can't push schema. Use a secret key: ${import_picocolors11.default.bold("sk_test_")} for the sandbox dev loop, or ${import_picocolors11.default.bold("sk_live_")} with ${import_picocolors11.default.bold("npx ablo push")} for a production deploy.`
283349
+ reason: `Restricted (${import_picocolors12.default.bold("rk_")}) keys can't push schema. Use a secret key: ${import_picocolors12.default.bold("sk_test_")} for the sandbox dev loop, or ${import_picocolors12.default.bold("sk_live_")} with ${import_picocolors12.default.bold("npx ablo push")} for a production deploy.`
283288
283350
  };
283289
283351
  }
283290
283352
  return {
283291
283353
  ok: false,
283292
- reason: `${import_picocolors11.default.bold("ABLO_API_KEY")} is not an Ablo key \u2014 expected ${import_picocolors11.default.bold("sk_test_\u2026")} (sandbox) or ${import_picocolors11.default.bold("sk_live_\u2026")} (production deploy via ${import_picocolors11.default.bold("npx ablo push")}).`
283354
+ reason: `${import_picocolors12.default.bold("ABLO_API_KEY")} is not an Ablo key \u2014 expected ${import_picocolors12.default.bold("sk_test_\u2026")} (sandbox) or ${import_picocolors12.default.bold("sk_live_\u2026")} (production deploy via ${import_picocolors12.default.bold("npx ablo push")}).`
283293
283355
  };
283294
283356
  }
283295
283357
  function wireEnvLocal(apiKey, cwd = process.cwd()) {
@@ -283299,7 +283361,7 @@ function wireEnvLocal(apiKey, cwd = process.cwd()) {
283299
283361
  if (!(0, import_fs7.existsSync)(envPath)) {
283300
283362
  (0, import_fs7.writeFileSync)(envPath, `${line}
283301
283363
  `, { mode: 384 });
283302
- action = `Created ${import_picocolors11.default.bold(".env.local")} with ${import_picocolors11.default.bold("ABLO_API_KEY")}`;
283364
+ action = `Created ${import_picocolors12.default.bold(".env.local")} with ${import_picocolors12.default.bold("ABLO_API_KEY")}`;
283303
283365
  } else {
283304
283366
  const content = (0, import_fs7.readFileSync)(envPath, "utf8");
283305
283367
  const match = /^ABLO_API_KEY=(.*)$/m.exec(content);
@@ -283307,12 +283369,12 @@ function wireEnvLocal(apiKey, cwd = process.cwd()) {
283307
283369
  if (!match) {
283308
283370
  (0, import_fs7.appendFileSync)(envPath, `${content.endsWith("\n") || content.length === 0 ? "" : "\n"}${line}
283309
283371
  `);
283310
- action = `Added ${import_picocolors11.default.bold("ABLO_API_KEY")} to ${import_picocolors11.default.bold(".env.local")}`;
283372
+ action = `Added ${import_picocolors12.default.bold("ABLO_API_KEY")} to ${import_picocolors12.default.bold(".env.local")}`;
283311
283373
  } else if (existing === apiKey) {
283312
- action = `${import_picocolors11.default.bold(".env.local")} already has this key`;
283374
+ action = `${import_picocolors12.default.bold(".env.local")} already has this key`;
283313
283375
  } else {
283314
283376
  (0, import_fs7.writeFileSync)(envPath, content.replace(/^ABLO_API_KEY=.*$/m, line));
283315
- action = `Updated ${import_picocolors11.default.bold("ABLO_API_KEY")} in ${import_picocolors11.default.bold(".env.local")} ${import_picocolors11.default.dim(`(was ${existing.slice(0, 12)}\u2026)`)}`;
283377
+ action = `Updated ${import_picocolors12.default.bold("ABLO_API_KEY")} in ${import_picocolors12.default.bold(".env.local")} ${import_picocolors12.default.dim(`(was ${existing.slice(0, 12)}\u2026)`)}`;
283316
283378
  }
283317
283379
  }
283318
283380
  const gitignorePath = (0, import_path5.resolve)(cwd, ".gitignore");
@@ -283326,7 +283388,7 @@ function wireEnvLocal(apiKey, cwd = process.cwd()) {
283326
283388
  `}.env.local
283327
283389
  `
283328
283390
  );
283329
- gitignoreNote = ` Added ${import_picocolors11.default.bold(".env.local")} to ${import_picocolors11.default.bold(".gitignore")} so the key can't be committed.`;
283391
+ gitignoreNote = ` Added ${import_picocolors12.default.bold(".env.local")} to ${import_picocolors12.default.bold(".gitignore")} so the key can't be committed.`;
283330
283392
  }
283331
283393
  return `${action}.${gitignoreNote}`;
283332
283394
  }
@@ -283341,7 +283403,7 @@ async function runPush(schema, args) {
283341
283403
  if (ok) {
283342
283404
  return {
283343
283405
  ok: true,
283344
- message: body.unchanged ? `schema unchanged ${import_picocolors11.default.dim(`(v${body.version})`)}` : `schema pushed (sandbox) ${import_picocolors11.default.dim(`(v${body.version}, hash ${body.hash})`)}`
283406
+ message: body.unchanged ? `schema unchanged ${import_picocolors12.default.dim(`(v${body.version})`)}` : `schema pushed (sandbox) ${import_picocolors12.default.dim(`(v${body.version}, hash ${body.hash})`)}`
283345
283407
  };
283346
283408
  }
283347
283409
  if (status2 === 409) {
@@ -283351,33 +283413,33 @@ async function runPush(schema, args) {
283351
283413
  (s) => s.shadowed != null
283352
283414
  );
283353
283415
  const lines = [
283354
- import_picocolors11.default.bold("Incompatible schema change \u2014 not safe to apply as-is."),
283416
+ import_picocolors12.default.bold("Incompatible schema change \u2014 not safe to apply as-is."),
283355
283417
  "",
283356
- ...unexecutable.map((u2) => import_picocolors11.default.red(fmtSignal(u2))),
283357
- ...warnings.map((w2) => import_picocolors11.default.yellow(fmtSignal(w2))),
283418
+ ...unexecutable.map((u2) => import_picocolors12.default.red(fmtSignal(u2))),
283419
+ ...warnings.map((w2) => import_picocolors12.default.yellow(fmtSignal(w2))),
283358
283420
  "",
283359
283421
  ...hasShadowed ? [
283360
- import_picocolors11.default.dim(
283422
+ import_picocolors12.default.dim(
283361
283423
  " These models exist in the baseline above but not in your push. Sandbox readers"
283362
283424
  ),
283363
- import_picocolors11.default.dim(
283425
+ import_picocolors12.default.dim(
283364
283426
  " fall back to the production schema until you push your own, so applying this drops them."
283365
283427
  ),
283366
283428
  ""
283367
283429
  ] : [],
283368
- import_picocolors11.default.dim(
283369
- ` Fix: ${import_picocolors11.default.bold("ablo push --force")} to apply anyway, or ${import_picocolors11.default.bold("--rename old:new")} if you renamed a model.`
283430
+ import_picocolors12.default.dim(
283431
+ ` Fix: ${import_picocolors12.default.bold("ablo push --force")} to apply anyway, or ${import_picocolors12.default.bold("--rename old:new")} if you renamed a model.`
283370
283432
  )
283371
283433
  ];
283372
283434
  return { ok: false, message: lines.join("\n") };
283373
283435
  }
283374
283436
  if (status2 === 403) {
283375
283437
  const serverSays = body.message ?? body.reason;
283376
- const hint = body.code === "database_role_cannot_enforce_rls" ? `Run ${import_picocolors11.default.bold("npx ablo migrate")} \u2014 it creates the scoped role for you (your DB credential never leaves this machine).` : `Schema authoring needs a ${import_picocolors11.default.bold("sandbox")} key with ${import_picocolors11.default.bold("schema:push")} \u2014 manage keys at ${import_picocolors11.default.cyan("https://abloatai.com")}.`;
283438
+ const hint = body.code === "database_role_cannot_enforce_rls" ? `Run ${import_picocolors12.default.bold("npx ablo migrate")} \u2014 it creates the scoped role for you (your DB credential never leaves this machine).` : `Schema authoring needs a ${import_picocolors12.default.bold("sandbox")} key with ${import_picocolors12.default.bold("schema:push")} \u2014 manage keys at ${import_picocolors12.default.cyan("https://abloatai.com")}.`;
283377
283439
  return {
283378
283440
  ok: false,
283379
283441
  message: `${serverSays ?? "This key can't author schema (missing schema:push scope)."}
283380
- ` + import_picocolors11.default.dim(hint)
283442
+ ` + import_picocolors12.default.dim(hint)
283381
283443
  };
283382
283444
  }
283383
283445
  return { ok: false, message: `Push failed (${status2}): ${body.message ?? body.reason ?? bodyText}` };
@@ -283387,25 +283449,25 @@ async function dev(argv) {
283387
283449
  try {
283388
283450
  args = parseDevArgs(argv);
283389
283451
  } catch (err) {
283390
- console.error(import_picocolors11.default.red(` ${err instanceof Error ? err.message : String(err)}`));
283452
+ console.error(import_picocolors12.default.red(` ${err instanceof Error ? err.message : String(err)}`));
283391
283453
  process.exit(1);
283392
283454
  }
283393
283455
  if (!args.apiKey) args.apiKey = resolveEffectiveApiKey("sandbox").key;
283394
283456
  const key = classifyKey(args.apiKey, getMode());
283395
283457
  if (!key.ok) {
283396
- console.error(import_picocolors11.default.red(` ${key.reason}`));
283458
+ console.error(import_picocolors12.default.red(` ${key.reason}`));
283397
283459
  process.exit(1);
283398
283460
  }
283399
283461
  console.log(`
283400
- ${brand("ablo")} ${import_picocolors11.default.dim("push")} ${import_picocolors11.default.dim("(sandbox)")}
283462
+ ${brand("ablo")} ${import_picocolors12.default.dim("push")} ${import_picocolors12.default.dim("(sandbox)")}
283401
283463
  `);
283402
283464
  const schema = await loadSchema(args.schemaPath, args.exportName);
283403
283465
  const modelCount = Object.keys(schema.models).length;
283404
283466
  console.log(
283405
- ` ${import_picocolors11.default.dim("schema")} ${import_picocolors11.default.bold(args.schemaPath)} ${import_picocolors11.default.dim(`(${modelCount} models, hash ${(0, import_schema5.schemaHash)(schema)})`)}`
283467
+ ` ${import_picocolors12.default.dim("schema")} ${import_picocolors12.default.bold(args.schemaPath)} ${import_picocolors12.default.dim(`(${modelCount} models, hash ${(0, import_schema5.schemaHash)(schema)})`)}`
283406
283468
  );
283407
- console.log(` ${import_picocolors11.default.dim("key")} ${args.apiKey.slice(0, 12)}\u2026`);
283408
- console.log(` ${import_picocolors11.default.dim("api")} ${args.url}
283469
+ console.log(` ${import_picocolors12.default.dim("key")} ${args.apiKey.slice(0, 12)}\u2026`);
283470
+ console.log(` ${import_picocolors12.default.dim("api")} ${args.url}
283409
283471
  `);
283410
283472
  const s = Y2();
283411
283473
  s.start("Pushing schema definition (sandbox)");
@@ -283414,16 +283476,16 @@ async function dev(argv) {
283414
283476
  if (!first.ok) process.exit(1);
283415
283477
  if (process.env.ABLO_API_KEY) {
283416
283478
  console.log(`
283417
- ${import_picocolors11.default.green("\u2713")} ${import_picocolors11.default.bold("ABLO_API_KEY")} is set in this shell \u2014 the SDK reads it directly.`);
283479
+ ${import_picocolors12.default.green("\u2713")} ${import_picocolors12.default.bold("ABLO_API_KEY")} is set in this shell \u2014 the SDK reads it directly.`);
283418
283480
  } else {
283419
283481
  console.log(`
283420
- ${import_picocolors11.default.green("\u2713")} ${wireEnvLocal(args.apiKey)}`);
283421
- console.log(` ${import_picocolors11.default.dim("Frameworks load it automatically; plain Node: node --env-file=.env.local app.ts")}`);
283482
+ ${import_picocolors12.default.green("\u2713")} ${wireEnvLocal(args.apiKey)}`);
283483
+ console.log(` ${import_picocolors12.default.dim("Frameworks load it automatically; plain Node: node --env-file=.env.local app.ts")}`);
283422
283484
  }
283423
283485
  console.log(` Your app is wired for the sandbox.`);
283424
283486
  if (!args.watch) return;
283425
283487
  const abs = (0, import_path5.resolve)(process.cwd(), args.schemaPath);
283426
- console.log(` ${import_picocolors11.default.dim(`watching ${args.schemaPath} \u2026 (Ctrl-C to stop)`)}
283488
+ console.log(` ${import_picocolors12.default.dim(`watching ${args.schemaPath} \u2026 (Ctrl-C to stop)`)}
283427
283489
  `);
283428
283490
  let timer2 = null;
283429
283491
  let pushing = false;
@@ -283443,7 +283505,7 @@ async function dev(argv) {
283443
283505
  const r2 = await runPush(next, args);
283444
283506
  s2.stop(r2.message, r2.ok ? 0 : 1);
283445
283507
  } catch (err) {
283446
- s2.stop(import_picocolors11.default.red(`schema reload failed: ${err instanceof Error ? err.message : String(err)}`), 1);
283508
+ s2.stop(import_picocolors12.default.red(`schema reload failed: ${err instanceof Error ? err.message : String(err)}`), 1);
283447
283509
  } finally {
283448
283510
  pushing = false;
283449
283511
  }
@@ -283451,7 +283513,7 @@ async function dev(argv) {
283451
283513
  const stop = () => {
283452
283514
  watcher.close();
283453
283515
  console.log(`
283454
- ${import_picocolors11.default.dim("stopped.")}`);
283516
+ ${import_picocolors12.default.dim("stopped.")}`);
283455
283517
  process.exit(0);
283456
283518
  };
283457
283519
  process.on("SIGINT", stop);
@@ -283463,7 +283525,7 @@ async function dev(argv) {
283463
283525
  // src/cli/login.ts
283464
283526
  init_cjs_shims();
283465
283527
  var import_child_process2 = require("child_process");
283466
- var import_picocolors12 = __toESM(require_picocolors(), 1);
283528
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
283467
283529
  init_dist2();
283468
283530
  init_config();
283469
283531
  init_theme();
@@ -283524,9 +283586,9 @@ async function deviceLogin(argv, deps = {}) {
283524
283586
  const code = await codeRes.json();
283525
283587
  const approvePath = `/cli?user_code=${code.user_code}`;
283526
283588
  const url = account === "signup" ? `${DASHBOARD_URL}/signup?next=${encodeURIComponent(approvePath)}` : `${DASHBOARD_URL}${approvePath}`;
283527
- Me(`${import_picocolors12.default.bold(code.user_code)}
283589
+ Me(`${import_picocolors13.default.bold(code.user_code)}
283528
283590
 
283529
- ${import_picocolors12.default.dim(url)}`, "Approve in your browser");
283591
+ ${import_picocolors13.default.dim(url)}`, "Approve in your browser");
283530
283592
  openUrl(url);
283531
283593
  const s = Y2();
283532
283594
  s.start("Waiting for approval\u2026");
@@ -283593,7 +283655,7 @@ ${import_picocolors12.default.dim(url)}`, "Approve in your browser");
283593
283655
  if (reason) M2.error(reason);
283594
283656
  else if (provRes) M2.error(`Key provisioning returned ${provRes.status} from ${DASHBOARD_URL}/api/cli/provision-key.`);
283595
283657
  M2.error(
283596
- `The browser approval succeeded but the key handoff failed. Try again, or grab a ${import_picocolors12.default.bold("sk_test_")} key from the dashboard and set ${import_picocolors12.default.bold("ABLO_API_KEY")}.`
283658
+ `The browser approval succeeded but the key handoff failed. Try again, or grab a ${import_picocolors13.default.bold("sk_test_")} key from the dashboard and set ${import_picocolors13.default.bold("ABLO_API_KEY")}.`
283597
283659
  );
283598
283660
  process.exit(1);
283599
283661
  }
@@ -283613,9 +283675,9 @@ ${import_picocolors12.default.dim(url)}`, "Approve in your browser");
283613
283675
  { mode: "sandbox", activeProject: prov.project ?? void 0 }
283614
283676
  );
283615
283677
  s.stop(`Saved keys to ${path}`);
283616
- const where = prov.project ? ` ${import_picocolors12.default.dim(`(project ${prov.project.slug})`)}` : "";
283678
+ const where = prov.project ? ` ${import_picocolors13.default.dim(`(project ${prov.project.slug})`)}` : "";
283617
283679
  Se(
283618
- `${import_picocolors12.default.green("\u2713")} Logged in ${import_picocolors12.default.dim("(sandbox)")}${where}. Run ${import_picocolors12.default.bold("npx ablo push")} to push your schema.`
283680
+ `${import_picocolors13.default.green("\u2713")} Logged in ${import_picocolors13.default.dim("(sandbox)")}${where}. Run ${import_picocolors13.default.bold("npx ablo push")} to push your schema.`
283619
283681
  );
283620
283682
  }
283621
283683
  async function login(argv = [], deps = {}) {
@@ -283624,13 +283686,13 @@ async function login(argv = [], deps = {}) {
283624
283686
  function logout() {
283625
283687
  const removed = clearCredential();
283626
283688
  if (removed) {
283627
- console.log(` ${import_picocolors12.default.green("\u2713")} Logged out ${import_picocolors12.default.dim(`(credentials removed from ${configDir()})`)}`);
283689
+ console.log(` ${import_picocolors13.default.green("\u2713")} Logged out ${import_picocolors13.default.dim(`(credentials removed from ${configDir()})`)}`);
283628
283690
  } else {
283629
- console.log(` ${import_picocolors12.default.dim("\u25CB")} Not logged in \u2014 nothing to remove.`);
283691
+ console.log(` ${import_picocolors13.default.dim("\u25CB")} Not logged in \u2014 nothing to remove.`);
283630
283692
  }
283631
283693
  if (process.env.ABLO_API_KEY) {
283632
283694
  console.log(
283633
- import_picocolors12.default.dim(` Note: ${import_picocolors12.default.bold("ABLO_API_KEY")} is still set in this shell and takes precedence.`)
283695
+ import_picocolors13.default.dim(` Note: ${import_picocolors13.default.bold("ABLO_API_KEY")} is still set in this shell and takes precedence.`)
283634
283696
  );
283635
283697
  }
283636
283698
  }
@@ -283640,7 +283702,7 @@ init_config();
283640
283702
 
283641
283703
  // src/cli/mode.ts
283642
283704
  init_cjs_shims();
283643
- var import_picocolors13 = __toESM(require_picocolors(), 1);
283705
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
283644
283706
  init_dist2();
283645
283707
  init_config();
283646
283708
  var PREFIX = { sandbox: "sk_test_", production: "rk_live_" };
@@ -283652,10 +283714,10 @@ function hintFor(m2, current) {
283652
283714
  }
283653
283715
  function apply(m2) {
283654
283716
  setMode(m2);
283655
- console.log(` ${import_picocolors13.default.green("\u2713")} now in ${import_picocolors13.default.bold(m2)}`);
283717
+ console.log(` ${import_picocolors14.default.green("\u2713")} now in ${import_picocolors14.default.bold(m2)}`);
283656
283718
  if (!getKeyEntry(m2)) {
283657
283719
  console.log(
283658
- import_picocolors13.default.dim(` No ${m2} key stored \u2014 run ${import_picocolors13.default.bold("ablo login")} or ${import_picocolors13.default.bold(`ablo login --api-key ${PREFIX[m2]}\u2026`)}.`)
283720
+ import_picocolors14.default.dim(` No ${m2} key stored \u2014 run ${import_picocolors14.default.bold("ablo login")} or ${import_picocolors14.default.bold(`ablo login --api-key ${PREFIX[m2]}\u2026`)}.`)
283659
283721
  );
283660
283722
  }
283661
283723
  }
@@ -283668,14 +283730,14 @@ async function mode(argv) {
283668
283730
  }
283669
283731
  if (arg) {
283670
283732
  console.error(
283671
- import_picocolors13.default.red(` unknown mode: ${arg}`) + import_picocolors13.default.dim(` (expected ${import_picocolors13.default.bold("sandbox")} or ${import_picocolors13.default.bold("production")})`)
283733
+ import_picocolors14.default.red(` unknown mode: ${arg}`) + import_picocolors14.default.dim(` (expected ${import_picocolors14.default.bold("sandbox")} or ${import_picocolors14.default.bold("production")})`)
283672
283734
  );
283673
283735
  process.exit(1);
283674
283736
  }
283675
283737
  const current = getMode();
283676
283738
  if (!process.stdin.isTTY || process.env.CI) {
283677
283739
  console.error(
283678
- import_picocolors13.default.red(" `ablo mode` needs an argument without a TTY: ") + import_picocolors13.default.bold("ablo mode sandbox") + import_picocolors13.default.dim(" | ") + import_picocolors13.default.bold("ablo mode production") + import_picocolors13.default.dim(` (current: ${current})`)
283740
+ import_picocolors14.default.red(" `ablo mode` needs an argument without a TTY: ") + import_picocolors14.default.bold("ablo mode sandbox") + import_picocolors14.default.dim(" | ") + import_picocolors14.default.bold("ablo mode production") + import_picocolors14.default.dim(` (current: ${current})`)
283679
283741
  );
283680
283742
  process.exit(1);
283681
283743
  }
@@ -283699,7 +283761,7 @@ init_projects();
283699
283761
 
283700
283762
  // src/cli/status.ts
283701
283763
  init_cjs_shims();
283702
- var import_picocolors14 = __toESM(require_picocolors(), 1);
283764
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
283703
283765
  init_config();
283704
283766
  init_target();
283705
283767
  init_theme();
@@ -283707,9 +283769,9 @@ init_push();
283707
283769
  function expiryLabel(iso) {
283708
283770
  const ms = Date.parse(iso) - Date.now();
283709
283771
  if (Number.isNaN(ms)) return "";
283710
- if (ms <= 0) return import_picocolors14.default.red("expired");
283772
+ if (ms <= 0) return import_picocolors15.default.red("expired");
283711
283773
  const days = Math.floor(ms / (24 * 60 * 60 * 1e3));
283712
- return import_picocolors14.default.dim(days > 0 ? `expires in ${days}d` : "expires <1d");
283774
+ return import_picocolors15.default.dim(days > 0 ? `expires in ${days}d` : "expires <1d");
283713
283775
  }
283714
283776
  async function ping(apiUrl2) {
283715
283777
  const ctrl = new AbortController();
@@ -283793,27 +283855,27 @@ function formatConflict(conflict) {
283793
283855
  function printTargetLines(target, localProject) {
283794
283856
  const confirmed = target?.confirmed ?? null;
283795
283857
  if (confirmed?.organizationId) {
283796
- console.log(` ${import_picocolors14.default.dim("org")} ${import_picocolors14.default.dim(confirmed.organizationId)}`);
283858
+ console.log(` ${import_picocolors15.default.dim("org")} ${import_picocolors15.default.dim(confirmed.organizationId)}`);
283797
283859
  }
283798
283860
  let projectLine;
283799
283861
  if (confirmed?.project) {
283800
283862
  const p2 = confirmed.project;
283801
- projectLine = p2.isDefault ? `${import_picocolors14.default.bold("default")} ${import_picocolors14.default.dim("(org-default)")}` : `${import_picocolors14.default.bold(p2.slug)} ${import_picocolors14.default.dim(`(${p2.id})`)}`;
283863
+ projectLine = p2.isDefault ? `${import_picocolors15.default.bold("default")} ${import_picocolors15.default.dim("(org-default)")}` : `${import_picocolors15.default.bold(p2.slug)} ${import_picocolors15.default.dim(`(${p2.id})`)}`;
283802
283864
  } else if (confirmed) {
283803
- projectLine = `${import_picocolors14.default.bold("default")} ${import_picocolors14.default.dim("(org-default)")}`;
283865
+ projectLine = `${import_picocolors15.default.bold("default")} ${import_picocolors15.default.dim("(org-default)")}`;
283804
283866
  } else if (localProject) {
283805
- projectLine = `${import_picocolors14.default.bold(localProject.slug)} ${import_picocolors14.default.dim(`(${localProject.id})`)} ${import_picocolors14.default.yellow("(unconfirmed)")}`;
283867
+ projectLine = `${import_picocolors15.default.bold(localProject.slug)} ${import_picocolors15.default.dim(`(${localProject.id})`)} ${import_picocolors15.default.yellow("(unconfirmed)")}`;
283806
283868
  } else {
283807
- projectLine = `${import_picocolors14.default.bold("default")} ${target ? import_picocolors14.default.yellow("(unconfirmed)") : import_picocolors14.default.dim("(org-default)")}`;
283869
+ projectLine = `${import_picocolors15.default.bold("default")} ${target ? import_picocolors15.default.yellow("(unconfirmed)") : import_picocolors15.default.dim("(org-default)")}`;
283808
283870
  }
283809
- console.log(` ${import_picocolors14.default.dim("project")} ${projectLine}`);
283871
+ console.log(` ${import_picocolors15.default.dim("project")} ${projectLine}`);
283810
283872
  const env = confirmed?.environment ?? target?.keyEnv ?? null;
283811
283873
  if (env) {
283812
- const suffix = confirmed ? "" : ` ${import_picocolors14.default.yellow("(unconfirmed)")}`;
283813
- console.log(` ${import_picocolors14.default.dim("env")} ${import_picocolors14.default.bold(env)}${suffix}`);
283874
+ const suffix = confirmed ? "" : ` ${import_picocolors15.default.yellow("(unconfirmed)")}`;
283875
+ console.log(` ${import_picocolors15.default.dim("env")} ${import_picocolors15.default.bold(env)}${suffix}`);
283814
283876
  }
283815
283877
  for (const m2 of target?.mismatches ?? []) {
283816
- console.log(` ${import_picocolors14.default.yellow("\u26A0")} ${import_picocolors14.default.yellow(describeMismatch(m2))}`);
283878
+ console.log(` ${import_picocolors15.default.yellow("\u26A0")} ${import_picocolors15.default.yellow(describeMismatch(m2))}`);
283817
283879
  }
283818
283880
  }
283819
283881
  async function status(args = []) {
@@ -283884,80 +283946,80 @@ async function status(args = []) {
283884
283946
  return;
283885
283947
  }
283886
283948
  console.log(`
283887
- ${brand("ablo")} ${import_picocolors14.default.dim("status")}
283949
+ ${brand("ablo")} ${import_picocolors15.default.dim("status")}
283888
283950
  `);
283889
283951
  if (effective.key && effective.source && effective.source !== "stored") {
283890
283952
  const label = effective.source === "env" ? "ABLO_API_KEY env" : effective.source;
283891
283953
  console.log(
283892
- ` ${import_picocolors14.default.dim("key")} ${effective.key.slice(0, 12)}\u2026 ${import_picocolors14.default.dim(`(${label} \u2014 overrides stored)`)}`
283954
+ ` ${import_picocolors15.default.dim("key")} ${effective.key.slice(0, 12)}\u2026 ${import_picocolors15.default.dim(`(${label} \u2014 overrides stored)`)}`
283893
283955
  );
283894
283956
  } else if (!cfg) {
283895
- console.log(` ${import_picocolors14.default.yellow("!")} Not logged in \u2014 run ${import_picocolors14.default.bold("ablo login")}.`);
283957
+ console.log(` ${import_picocolors15.default.yellow("!")} Not logged in \u2014 run ${import_picocolors15.default.bold("ablo login")}.`);
283896
283958
  }
283897
- console.log(` ${import_picocolors14.default.dim("mode")} ${import_picocolors14.default.bold(mode2)}`);
283959
+ console.log(` ${import_picocolors15.default.dim("mode")} ${import_picocolors15.default.bold(mode2)}`);
283898
283960
  const activeEntry = getKeyEntry(mode2);
283899
283961
  const key = describeEffectiveKey(mode2, process.env.ABLO_API_KEY, activeEntry);
283900
283962
  if (key.keyMismatch) {
283901
- console.log(` ${import_picocolors14.default.yellow("!")} ${import_picocolors14.default.yellow(key.keyMismatch.message)}`);
283963
+ console.log(` ${import_picocolors15.default.yellow("!")} ${import_picocolors15.default.yellow(key.keyMismatch.message)}`);
283902
283964
  }
283903
283965
  const activeProject = getActiveProject();
283904
283966
  printTargetLines(target, activeProject);
283905
283967
  for (const m2 of ["sandbox", "production"]) {
283906
283968
  const entry = getKeyEntry(m2);
283907
- const marker = m2 === mode2 ? import_picocolors14.default.green("\u25CF") : import_picocolors14.default.dim("\u25CB");
283969
+ const marker = m2 === mode2 ? import_picocolors15.default.green("\u25CF") : import_picocolors15.default.dim("\u25CB");
283908
283970
  if (entry) {
283909
283971
  const exp = entry.expiresAt ? ` ${expiryLabel(entry.expiresAt)}` : "";
283910
- console.log(` ${marker} ${m2.padEnd(10)} ${import_picocolors14.default.dim(`${entry.apiKey.slice(0, 12)}\u2026`)}${exp}`);
283972
+ console.log(` ${marker} ${m2.padEnd(10)} ${import_picocolors15.default.dim(`${entry.apiKey.slice(0, 12)}\u2026`)}${exp}`);
283911
283973
  } else {
283912
- console.log(` ${marker} ${m2.padEnd(10)} ${import_picocolors14.default.dim("\u2014 no key")}`);
283974
+ console.log(` ${marker} ${m2.padEnd(10)} ${import_picocolors15.default.dim("\u2014 no key")}`);
283913
283975
  }
283914
283976
  }
283915
283977
  const org = target?.confirmed?.organizationId ?? activeEntry?.organizationId;
283916
283978
  const plan = resolvePushPlan();
283917
283979
  console.log(
283918
- ` ${import_picocolors14.default.dim("push")} ${plan.apiKey ? `${import_picocolors14.default.bold(plan.flow)} ${import_picocolors14.default.dim(`with ${plan.apiKey.slice(0, 12)}\u2026 (${plan.source})`)}` : `${import_picocolors14.default.bold(plan.flow)} ${import_picocolors14.default.yellow("\u2014 no credential")} ${import_picocolors14.default.dim(`(run ${import_picocolors14.default.bold("ablo login")} or set ${import_picocolors14.default.bold("ABLO_API_KEY")})`)}`}`
283980
+ ` ${import_picocolors15.default.dim("push")} ${plan.apiKey ? `${import_picocolors15.default.bold(plan.flow)} ${import_picocolors15.default.dim(`with ${plan.apiKey.slice(0, 12)}\u2026 (${plan.source})`)}` : `${import_picocolors15.default.bold(plan.flow)} ${import_picocolors15.default.yellow("\u2014 no credential")} ${import_picocolors15.default.dim(`(run ${import_picocolors15.default.bold("ablo login")} or set ${import_picocolors15.default.bold("ABLO_API_KEY")})`)}`}`
283919
283981
  );
283920
- process.stdout.write(` ${import_picocolors14.default.dim("api")} ${apiUrl2} `);
283982
+ process.stdout.write(` ${import_picocolors15.default.dim("api")} ${apiUrl2} `);
283921
283983
  const reachable = await ping(apiUrl2);
283922
- console.log(reachable ? import_picocolors14.default.green("reachable") : import_picocolors14.default.red("unreachable"));
283984
+ console.log(reachable ? import_picocolors15.default.green("reachable") : import_picocolors15.default.red("unreachable"));
283923
283985
  if (reachable) {
283924
283986
  const introspectKey = effective.key;
283925
283987
  const pushed = await fetchPushedSchema(apiUrl2, introspectKey);
283926
283988
  if (pushed?.active) {
283927
- const when = pushed.pushedAt ? ` ${import_picocolors14.default.dim(`@ ${pushed.pushedAt.slice(0, 10)}`)}` : "";
283928
- const ver = pushed.version != null ? ` ${import_picocolors14.default.dim(`(rev ${pushed.version})`)}` : "";
283929
- const hashLabel = pushed.hash ? ` ${import_picocolors14.default.dim(`hash ${pushed.hash}`)}` : "";
283930
- console.log(` ${import_picocolors14.default.dim("schema")} ${import_picocolors14.default.bold(`${pushed.models.length} models pushed`)}${ver}${hashLabel}${when}`);
283989
+ const when = pushed.pushedAt ? ` ${import_picocolors15.default.dim(`@ ${pushed.pushedAt.slice(0, 10)}`)}` : "";
283990
+ const ver = pushed.version != null ? ` ${import_picocolors15.default.dim(`(rev ${pushed.version})`)}` : "";
283991
+ const hashLabel = pushed.hash ? ` ${import_picocolors15.default.dim(`hash ${pushed.hash}`)}` : "";
283992
+ console.log(` ${import_picocolors15.default.dim("schema")} ${import_picocolors15.default.bold(`${pushed.models.length} models pushed`)}${ver}${hashLabel}${when}`);
283931
283993
  for (const m2 of pushed.models) {
283932
- const tn = m2.typename === m2.key ? import_picocolors14.default.dim(`typename=${m2.typename}`) : import_picocolors14.default.yellow(`typename=${m2.typename}`);
283994
+ const tn = m2.typename === m2.key ? import_picocolors15.default.dim(`typename=${m2.typename}`) : import_picocolors15.default.yellow(`typename=${m2.typename}`);
283933
283995
  const conflict = formatConflict(m2.conflict);
283934
- const conflictStr2 = conflict ? ` ${import_picocolors14.default.dim(`conflict=${conflict}`)}` : "";
283935
- console.log(` ${import_picocolors14.default.dim("\u2022")} ${m2.key.padEnd(14)} ${tn}${conflictStr2}`);
283996
+ const conflictStr2 = conflict ? ` ${import_picocolors15.default.dim(`conflict=${conflict}`)}` : "";
283997
+ console.log(` ${import_picocolors15.default.dim("\u2022")} ${m2.key.padEnd(14)} ${tn}${conflictStr2}`);
283936
283998
  }
283937
283999
  } else if (pushed && !pushed.active) {
283938
- console.log(` ${import_picocolors14.default.dim("schema")} ${import_picocolors14.default.yellow("none pushed")} ${import_picocolors14.default.dim(`(run ${import_picocolors14.default.bold("ablo push")} or ${import_picocolors14.default.bold("ablo dev")})`)}`);
284000
+ console.log(` ${import_picocolors15.default.dim("schema")} ${import_picocolors15.default.yellow("none pushed")} ${import_picocolors15.default.dim(`(run ${import_picocolors15.default.bold("ablo push")} or ${import_picocolors15.default.bold("ablo dev")})`)}`);
283939
284001
  }
283940
284002
  const firstPushedModel = pushed?.active ? pushed.models[0] : void 0;
283941
284003
  if (firstPushedModel !== void 0) {
283942
284004
  const probe = await probeDataPlane(apiUrl2, introspectKey, firstPushedModel.typename);
283943
284005
  if (probe.status === "no_database") {
283944
- console.log(` ${import_picocolors14.default.dim("data")} ${import_picocolors14.default.red("\u2717 no database registered")}${org ? import_picocolors14.default.dim(` for org ${org}`) : ""}`);
284006
+ console.log(` ${import_picocolors15.default.dim("data")} ${import_picocolors15.default.red("\u2717 no database registered")}${org ? import_picocolors15.default.dim(` for org ${org}`) : ""}`);
283945
284007
  console.log(
283946
- ` ${import_picocolors14.default.dim(
283947
- `reads/writes will fail with ${import_picocolors14.default.bold("tenant_routing_failed")}. Connect one with ${import_picocolors14.default.bold("ablo connect")}, or point ${import_picocolors14.default.bold("ABLO_API_KEY")} at an org that has a database.`
284008
+ ` ${import_picocolors15.default.dim(
284009
+ `reads/writes will fail with ${import_picocolors15.default.bold("tenant_routing_failed")}. Connect one with ${import_picocolors15.default.bold("ablo connect")}, or point ${import_picocolors15.default.bold("ABLO_API_KEY")} at an org that has a database.`
283948
284010
  )}`
283949
284011
  );
283950
284012
  } else if (probe.status === "intermittent") {
283951
- console.log(` ${import_picocolors14.default.dim("data")} ${import_picocolors14.default.red(`\u2717 database routing is intermittent`)} ${import_picocolors14.default.dim(`(${probe.ok} ok / ${probe.failed} failed of ${probe.ok + probe.failed})`)}${org ? import_picocolors14.default.dim(` for org ${org}`) : ""}`);
284013
+ console.log(` ${import_picocolors15.default.dim("data")} ${import_picocolors15.default.red(`\u2717 database routing is intermittent`)} ${import_picocolors15.default.dim(`(${probe.ok} ok / ${probe.failed} failed of ${probe.ok + probe.failed})`)}${org ? import_picocolors15.default.dim(` for org ${org}`) : ""}`);
283952
284014
  console.log(
283953
- ` ${import_picocolors14.default.dim(
283954
- `some reads/writes fail with ${import_picocolors14.default.bold("tenant_routing_failed")} \u2014 the registration is unstable. Re-establish it with ${import_picocolors14.default.bold("ablo connect")} (or check for a recent server redeploy).`
284015
+ ` ${import_picocolors15.default.dim(
284016
+ `some reads/writes fail with ${import_picocolors15.default.bold("tenant_routing_failed")} \u2014 the registration is unstable. Re-establish it with ${import_picocolors15.default.bold("ablo connect")} (or check for a recent server redeploy).`
283955
284017
  )}`
283956
284018
  );
283957
284019
  } else if (probe.status === "forbidden") {
283958
- console.log(` ${import_picocolors14.default.dim("data")} ${import_picocolors14.default.yellow("? key not authorized to read")}${probe.detail ? import_picocolors14.default.dim(` (${probe.detail})`) : ""}`);
284020
+ console.log(` ${import_picocolors15.default.dim("data")} ${import_picocolors15.default.yellow("? key not authorized to read")}${probe.detail ? import_picocolors15.default.dim(` (${probe.detail})`) : ""}`);
283959
284021
  } else if (probe.status === "unknown") {
283960
- console.log(` ${import_picocolors14.default.dim("data")} ${import_picocolors14.default.yellow(`? data-plane check inconclusive (${probe.detail})`)}`);
284022
+ console.log(` ${import_picocolors15.default.dim("data")} ${import_picocolors15.default.yellow(`? data-plane check inconclusive (${probe.detail})`)}`);
283961
284023
  }
283962
284024
  }
283963
284025
  }
@@ -283967,7 +284029,7 @@ async function status(args = []) {
283967
284029
  // src/cli/logs.ts
283968
284030
  init_cjs_shims();
283969
284031
  init_errors();
283970
- var import_picocolors15 = __toESM(require_picocolors(), 1);
284032
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
283971
284033
  init_config();
283972
284034
  init_theme();
283973
284035
  init_push();
@@ -284034,10 +284096,10 @@ function resolveSince(since) {
284034
284096
  var sleep2 = (ms) => new Promise((r2) => setTimeout(r2, ms));
284035
284097
  function colorOp(op) {
284036
284098
  const label = op.padEnd(6);
284037
- if (op === "create") return import_picocolors15.default.green(label);
284038
- if (op === "update") return import_picocolors15.default.yellow(label);
284039
- if (op === "delete") return import_picocolors15.default.red(label);
284040
- return import_picocolors15.default.dim(label);
284099
+ if (op === "create") return import_picocolors16.default.green(label);
284100
+ if (op === "update") return import_picocolors16.default.yellow(label);
284101
+ if (op === "delete") return import_picocolors16.default.red(label);
284102
+ return import_picocolors16.default.dim(label);
284041
284103
  }
284042
284104
  function render(e2, json) {
284043
284105
  if (json) {
@@ -284046,21 +284108,21 @@ function render(e2, json) {
284046
284108
  return;
284047
284109
  }
284048
284110
  const t = new Date(e2.at).toLocaleTimeString();
284049
- const actor = e2.actor ? import_picocolors15.default.dim(` ${e2.actor}`) : "";
284050
- console.log(` ${import_picocolors15.default.dim(t)} ${colorOp(e2.op)} ${import_picocolors15.default.bold(e2.model)} ${import_picocolors15.default.dim(e2.recordId)}${actor}`);
284111
+ const actor = e2.actor ? import_picocolors16.default.dim(` ${e2.actor}`) : "";
284112
+ console.log(` ${import_picocolors16.default.dim(t)} ${colorOp(e2.op)} ${import_picocolors16.default.bold(e2.model)} ${import_picocolors16.default.dim(e2.recordId)}${actor}`);
284051
284113
  }
284052
284114
  async function logs(argv) {
284053
284115
  let args;
284054
284116
  try {
284055
284117
  args = parseLogsArgs(argv);
284056
284118
  } catch (err) {
284057
- console.error(import_picocolors15.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284119
+ console.error(import_picocolors16.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284058
284120
  process.exit(1);
284059
284121
  }
284060
284122
  const apiKey = resolveApiKey(args.mode);
284061
284123
  if (!apiKey) {
284062
284124
  console.error(
284063
- import_picocolors15.default.red(` No API key.`) + import_picocolors15.default.dim(` Run ${import_picocolors15.default.bold("ablo login")} or set ${import_picocolors15.default.bold("ABLO_API_KEY")}.`)
284125
+ import_picocolors16.default.red(` No API key.`) + import_picocolors16.default.dim(` Run ${import_picocolors16.default.bold("ablo login")} or set ${import_picocolors16.default.bold("ABLO_API_KEY")}.`)
284064
284126
  );
284065
284127
  process.exit(1);
284066
284128
  }
@@ -284074,7 +284136,7 @@ async function logs(argv) {
284074
284136
  if (!res) return null;
284075
284137
  if (!res.ok) {
284076
284138
  const body = await res.json().catch(() => ({}));
284077
- console.error(import_picocolors15.default.red(` logs failed (${res.status}): ${body.reason ?? body.message ?? ""}`));
284139
+ console.error(import_picocolors16.default.red(` logs failed (${res.status}): ${body.reason ?? body.message ?? ""}`));
284078
284140
  process.exit(1);
284079
284141
  }
284080
284142
  const json = await res.json();
@@ -284085,7 +284147,7 @@ async function logs(argv) {
284085
284147
  }
284086
284148
  if (!args.json) {
284087
284149
  console.log(`
284088
- ${brand("ablo")} ${import_picocolors15.default.dim("logs")} ${import_picocolors15.default.dim(`(${args.mode ?? "active"} mode)`)}
284150
+ ${brand("ablo")} ${import_picocolors16.default.dim("logs")} ${import_picocolors16.default.dim(`(${args.mode ?? "active"} mode)`)}
284089
284151
  `);
284090
284152
  }
284091
284153
  const initial = await fetchPage({
@@ -284095,13 +284157,13 @@ async function logs(argv) {
284095
284157
  ...args.op ? { op: args.op } : {}
284096
284158
  });
284097
284159
  if (!initial) {
284098
- console.error(import_picocolors15.default.red(` Couldn't reach ${baseUrl2}.`));
284160
+ console.error(import_picocolors16.default.red(` Couldn't reach ${baseUrl2}.`));
284099
284161
  process.exit(1);
284100
284162
  }
284101
284163
  for (const e2 of initial.events) render(e2, args.json);
284102
284164
  let cursor = initial.cursor;
284103
284165
  if (!args.follow) return;
284104
- if (!args.json) console.log(` ${import_picocolors15.default.dim("watching for new activity \u2026 (Ctrl-C to stop)")}
284166
+ if (!args.json) console.log(` ${import_picocolors16.default.dim("watching for new activity \u2026 (Ctrl-C to stop)")}
284105
284167
  `);
284106
284168
  for (; ; ) {
284107
284169
  await sleep2(1500);
@@ -284119,7 +284181,7 @@ async function logs(argv) {
284119
284181
  // src/cli/webhooks.ts
284120
284182
  init_cjs_shims();
284121
284183
  var import_fs8 = require("fs");
284122
- var import_picocolors16 = __toESM(require_picocolors(), 1);
284184
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
284123
284185
  init_credentialPolicy();
284124
284186
  init_config();
284125
284187
  init_theme();
@@ -284152,12 +284214,12 @@ function requireKey2(mode2) {
284152
284214
  const apiKey = resolveApiKey(mode2);
284153
284215
  if (!apiKey) {
284154
284216
  console.error(
284155
- import_picocolors16.default.red(" No API key.") + import_picocolors16.default.dim(` Run ${import_picocolors16.default.bold("ablo login")} or set ${import_picocolors16.default.bold("ABLO_API_KEY")}.`)
284217
+ import_picocolors17.default.red(" No API key.") + import_picocolors17.default.dim(` Run ${import_picocolors17.default.bold("ablo login")} or set ${import_picocolors17.default.bold("ABLO_API_KEY")}.`)
284156
284218
  );
284157
284219
  process.exit(1);
284158
284220
  }
284159
284221
  if (classifyCredentialKind(apiKey) !== "secret") {
284160
- console.error(import_picocolors16.default.red(" Managing webhooks requires a secret key ") + import_picocolors16.default.dim("(sk_test_ / sk_live_)."));
284222
+ console.error(import_picocolors17.default.red(" Managing webhooks requires a secret key ") + import_picocolors17.default.dim("(sk_test_ / sk_live_)."));
284161
284223
  process.exit(1);
284162
284224
  }
284163
284225
  return apiKey;
@@ -284173,12 +284235,12 @@ async function api(apiKey, method, path, body) {
284173
284235
  ...body ? { body: JSON.stringify(body) } : {}
284174
284236
  }).catch(() => null);
284175
284237
  if (!res) {
284176
- console.error(import_picocolors16.default.red(` Couldn't reach ${baseUrl()}.`));
284238
+ console.error(import_picocolors17.default.red(` Couldn't reach ${baseUrl()}.`));
284177
284239
  process.exit(1);
284178
284240
  }
284179
284241
  if (!res.ok) {
284180
284242
  const err = await res.json().catch(() => ({}));
284181
- console.error(import_picocolors16.default.red(` Request failed (${res.status}): ${err.message ?? err.reason ?? ""}`));
284243
+ console.error(import_picocolors17.default.red(` Request failed (${res.status}): ${err.message ?? err.reason ?? ""}`));
284182
284244
  process.exit(1);
284183
284245
  }
284184
284246
  return await res.json();
@@ -284200,11 +284262,11 @@ ${line}
284200
284262
  return file;
284201
284263
  }
284202
284264
  function printEndpoint(e2) {
284203
- const dot = e2.status === "enabled" ? import_picocolors16.default.green("\u25CF") : import_picocolors16.default.red("\u25CF");
284204
- const health = e2.last_error ? import_picocolors16.default.red(` last error: ${e2.last_error}`) : "";
284205
- console.log(` ${dot} ${import_picocolors16.default.bold(e2.id)} ${e2.url}`);
284265
+ const dot = e2.status === "enabled" ? import_picocolors17.default.green("\u25CF") : import_picocolors17.default.red("\u25CF");
284266
+ const health = e2.last_error ? import_picocolors17.default.red(` last error: ${e2.last_error}`) : "";
284267
+ console.log(` ${dot} ${import_picocolors17.default.bold(e2.id)} ${e2.url}`);
284206
284268
  console.log(
284207
- import_picocolors16.default.dim(
284269
+ import_picocolors17.default.dim(
284208
284270
  ` ${e2.status} \xB7 ${e2.environment} \xB7 events ${e2.enabled_events.join(",")} \xB7 cursor ${e2.cursor ?? "\u2014"}${health}`
284209
284271
  )
284210
284272
  );
@@ -284216,7 +284278,7 @@ async function webhooks(argv) {
284216
284278
  if (sub === "create") {
284217
284279
  const url = positional(rest);
284218
284280
  if (!url) {
284219
- console.error(import_picocolors16.default.red(" Usage: ") + brand("ablo webhooks create <url>"));
284281
+ console.error(import_picocolors17.default.red(" Usage: ") + brand("ablo webhooks create <url>"));
284220
284282
  process.exit(1);
284221
284283
  }
284222
284284
  const apiKey = requireKey2(mode2);
@@ -284228,8 +284290,8 @@ async function webhooks(argv) {
284228
284290
  });
284229
284291
  const file = writeSecretToEnv(created.secret);
284230
284292
  console.log(`
284231
- ${import_picocolors16.default.green("\u2713")} Registered ${import_picocolors16.default.bold(created.id)} \u2192 ${created.url}`);
284232
- console.log(` ${import_picocolors16.default.green("\u2713")} Wrote ${import_picocolors16.default.bold(ENV_KEY)} to ${import_picocolors16.default.bold(file)} ${import_picocolors16.default.dim("(shown once)")}
284293
+ ${import_picocolors17.default.green("\u2713")} Registered ${import_picocolors17.default.bold(created.id)} \u2192 ${created.url}`);
284294
+ console.log(` ${import_picocolors17.default.green("\u2713")} Wrote ${import_picocolors17.default.bold(ENV_KEY)} to ${import_picocolors17.default.bold(file)} ${import_picocolors17.default.dim("(shown once)")}
284233
284295
  `);
284234
284296
  return;
284235
284297
  }
@@ -284237,7 +284299,7 @@ async function webhooks(argv) {
284237
284299
  const apiKey = requireKey2(mode2);
284238
284300
  const { data } = await api(apiKey, "GET", "");
284239
284301
  if (data.length === 0) {
284240
- console.log(import_picocolors16.default.dim(" No webhook endpoints. ") + brand("ablo webhooks create <url>"));
284302
+ console.log(import_picocolors17.default.dim(" No webhook endpoints. ") + brand("ablo webhooks create <url>"));
284241
284303
  return;
284242
284304
  }
284243
284305
  console.log();
@@ -284248,40 +284310,40 @@ async function webhooks(argv) {
284248
284310
  if (sub === "roll") {
284249
284311
  const id = positional(rest);
284250
284312
  if (!id) {
284251
- console.error(import_picocolors16.default.red(" Usage: ") + brand("ablo webhooks roll <id>"));
284313
+ console.error(import_picocolors17.default.red(" Usage: ") + brand("ablo webhooks roll <id>"));
284252
284314
  process.exit(1);
284253
284315
  }
284254
284316
  const apiKey = requireKey2(mode2);
284255
284317
  const rolled = await api(apiKey, "POST", `/${id}/roll_secret`);
284256
284318
  const file = writeSecretToEnv(rolled.secret);
284257
284319
  console.log(`
284258
- ${import_picocolors16.default.green("\u2713")} Rolled secret for ${import_picocolors16.default.bold(id)} \u2192 ${import_picocolors16.default.bold(file)} ${import_picocolors16.default.dim("(old secret now invalid)")}
284320
+ ${import_picocolors17.default.green("\u2713")} Rolled secret for ${import_picocolors17.default.bold(id)} \u2192 ${import_picocolors17.default.bold(file)} ${import_picocolors17.default.dim("(old secret now invalid)")}
284259
284321
  `);
284260
284322
  return;
284261
284323
  }
284262
284324
  if (sub === "enable") {
284263
284325
  const id = positional(rest);
284264
284326
  if (!id) {
284265
- console.error(import_picocolors16.default.red(" Usage: ") + brand("ablo webhooks enable <id>"));
284327
+ console.error(import_picocolors17.default.red(" Usage: ") + brand("ablo webhooks enable <id>"));
284266
284328
  process.exit(1);
284267
284329
  }
284268
284330
  const apiKey = requireKey2(mode2);
284269
284331
  const e2 = await api(apiKey, "POST", `/${id}/enable`);
284270
- console.log(` ${import_picocolors16.default.green("\u2713")} Re-enabled ${import_picocolors16.default.bold(e2.id)}`);
284332
+ console.log(` ${import_picocolors17.default.green("\u2713")} Re-enabled ${import_picocolors17.default.bold(e2.id)}`);
284271
284333
  return;
284272
284334
  }
284273
284335
  if (sub === "rm" || sub === "delete") {
284274
284336
  const id = positional(rest);
284275
284337
  if (!id) {
284276
- console.error(import_picocolors16.default.red(" Usage: ") + brand("ablo webhooks rm <id>"));
284338
+ console.error(import_picocolors17.default.red(" Usage: ") + brand("ablo webhooks rm <id>"));
284277
284339
  process.exit(1);
284278
284340
  }
284279
284341
  const apiKey = requireKey2(mode2);
284280
284342
  await api(apiKey, "DELETE", `/${id}`);
284281
- console.log(` ${import_picocolors16.default.green("\u2713")} Removed ${import_picocolors16.default.bold(id)}`);
284343
+ console.log(` ${import_picocolors17.default.green("\u2713")} Removed ${import_picocolors17.default.bold(id)}`);
284282
284344
  return;
284283
284345
  }
284284
- console.log(` ${import_picocolors16.default.bold("Usage:")}`);
284346
+ console.log(` ${import_picocolors17.default.bold("Usage:")}`);
284285
284347
  console.log(` ${brand("ablo webhooks create <url>")} Register an endpoint; writes ${ENV_KEY}`);
284286
284348
  console.log(` ${brand("ablo webhooks list")} List endpoints + delivery health`);
284287
284349
  console.log(` ${brand("ablo webhooks roll <id>")} Mint a fresh signing secret`);
@@ -284293,7 +284355,7 @@ async function webhooks(argv) {
284293
284355
  // src/cli/check.ts
284294
284356
  init_cjs_shims();
284295
284357
  init_errors();
284296
- var import_picocolors17 = __toESM(require_picocolors(), 1);
284358
+ var import_picocolors18 = __toESM(require_picocolors(), 1);
284297
284359
  init_src();
284298
284360
  var import_schema6 = require("@abloatai/ablo/schema");
284299
284361
  init_push();
@@ -284331,13 +284393,13 @@ async function check(argv) {
284331
284393
  try {
284332
284394
  args = parseCheckArgs(argv);
284333
284395
  } catch (err) {
284334
- console.error(import_picocolors17.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284396
+ console.error(import_picocolors18.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284335
284397
  process.exit(1);
284336
284398
  }
284337
284399
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
284338
284400
  if (!dbUrl) {
284339
284401
  console.error(
284340
- import_picocolors17.default.red(` No database.`) + import_picocolors17.default.dim(` Set ${import_picocolors17.default.bold("DATABASE_URL")} to the Postgres you want Ablo to adopt.`)
284402
+ import_picocolors18.default.red(` No database.`) + import_picocolors18.default.dim(` Set ${import_picocolors18.default.bold("DATABASE_URL")} to the Postgres you want Ablo to adopt.`)
284341
284403
  );
284342
284404
  process.exit(1);
284343
284405
  }
@@ -284352,7 +284414,7 @@ async function check(argv) {
284352
284414
  [args.appSchema]
284353
284415
  );
284354
284416
  } catch (err) {
284355
- console.error(import_picocolors17.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
284417
+ console.error(import_picocolors18.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
284356
284418
  await sql.end({ timeout: 2 });
284357
284419
  process.exit(1);
284358
284420
  }
@@ -284367,7 +284429,7 @@ async function check(argv) {
284367
284429
  set.add(r2.column_name);
284368
284430
  }
284369
284431
  console.log(`
284370
- ${brand("ablo")} ${import_picocolors17.default.dim("check")} ${import_picocolors17.default.dim(`schema "${args.appSchema}"`)}
284432
+ ${brand("ablo")} ${import_picocolors18.default.dim("check")} ${import_picocolors18.default.dim(`schema "${args.appSchema}"`)}
284371
284433
  `);
284372
284434
  const declaredTables = /* @__PURE__ */ new Set();
284373
284435
  let errors = 0;
@@ -284377,7 +284439,7 @@ async function check(argv) {
284377
284439
  declaredTables.add(table);
284378
284440
  const present = colsByTable.get(table);
284379
284441
  if (!present) {
284380
- console.log(` ${import_picocolors17.default.red("\u2717")} ${import_picocolors17.default.bold(key)} ${import_picocolors17.default.dim("\u2192")} table ${import_picocolors17.default.bold(table)} ${import_picocolors17.default.red("not found")}`);
284442
+ console.log(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.bold(key)} ${import_picocolors18.default.dim("\u2192")} table ${import_picocolors18.default.bold(table)} ${import_picocolors18.default.red("not found")}`);
284381
284443
  errors++;
284382
284444
  continue;
284383
284445
  }
@@ -284399,26 +284461,26 @@ async function check(argv) {
284399
284461
  if (!present.has(col)) problems.push(`missing column "${col}" (field ${fieldName})`);
284400
284462
  }
284401
284463
  if (problems.length > 0) {
284402
- console.log(` ${import_picocolors17.default.red("\u2717")} ${import_picocolors17.default.bold(key)} ${import_picocolors17.default.dim("\u2192")} ${table}`);
284403
- for (const p2 of problems) console.log(` ${import_picocolors17.default.red("\u2022")} ${p2}`);
284404
- for (const w2 of warns) console.log(` ${import_picocolors17.default.yellow("\u2022")} ${w2}`);
284464
+ console.log(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.bold(key)} ${import_picocolors18.default.dim("\u2192")} ${table}`);
284465
+ for (const p2 of problems) console.log(` ${import_picocolors18.default.red("\u2022")} ${p2}`);
284466
+ for (const w2 of warns) console.log(` ${import_picocolors18.default.yellow("\u2022")} ${w2}`);
284405
284467
  errors++;
284406
284468
  } else if (warns.length > 0) {
284407
- console.log(` ${import_picocolors17.default.yellow("!")} ${import_picocolors17.default.bold(key)} ${import_picocolors17.default.dim("\u2192")} ${table}`);
284408
- for (const w2 of warns) console.log(` ${import_picocolors17.default.yellow("\u2022")} ${w2}`);
284469
+ console.log(` ${import_picocolors18.default.yellow("!")} ${import_picocolors18.default.bold(key)} ${import_picocolors18.default.dim("\u2192")} ${table}`);
284470
+ for (const w2 of warns) console.log(` ${import_picocolors18.default.yellow("\u2022")} ${w2}`);
284409
284471
  warnings++;
284410
284472
  } else {
284411
- console.log(` ${import_picocolors17.default.green("\u2713")} ${import_picocolors17.default.bold(key)} ${import_picocolors17.default.dim(`\u2192 ${table} (id, ${orgCol ?? "no org"} ok)`)}`);
284473
+ console.log(` ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.bold(key)} ${import_picocolors18.default.dim(`\u2192 ${table} (id, ${orgCol ?? "no org"} ok)`)}`);
284412
284474
  }
284413
284475
  }
284414
284476
  const modelCount = Object.keys(schemaJson.models).length;
284415
284477
  const ignored = [...colsByTable.keys()].filter((t) => !declaredTables.has(t)).length;
284416
284478
  console.log(
284417
284479
  `
284418
- ${modelCount} model${modelCount === 1 ? "" : "s"} \xB7 ${import_picocolors17.default.green(`${modelCount - errors - warnings} ok`)}` + (warnings ? ` \xB7 ${import_picocolors17.default.yellow(`${warnings} warning${warnings === 1 ? "" : "s"}`)}` : "") + (errors ? ` \xB7 ${import_picocolors17.default.red(`${errors} error${errors === 1 ? "" : "s"}`)}` : "")
284480
+ ${modelCount} model${modelCount === 1 ? "" : "s"} \xB7 ${import_picocolors18.default.green(`${modelCount - errors - warnings} ok`)}` + (warnings ? ` \xB7 ${import_picocolors18.default.yellow(`${warnings} warning${warnings === 1 ? "" : "s"}`)}` : "") + (errors ? ` \xB7 ${import_picocolors18.default.red(`${errors} error${errors === 1 ? "" : "s"}`)}` : "")
284419
284481
  );
284420
284482
  if (ignored > 0) {
284421
- console.log(` ${import_picocolors17.default.dim(`${ignored} other table${ignored === 1 ? "" : "s"} in your database \u2014 ignored by Ablo`)}`);
284483
+ console.log(` ${import_picocolors18.default.dim(`${ignored} other table${ignored === 1 ? "" : "s"} in your database \u2014 ignored by Ablo`)}`);
284422
284484
  }
284423
284485
  console.log();
284424
284486
  process.exit(errors > 0 ? 1 : 0);
@@ -284426,7 +284488,7 @@ async function check(argv) {
284426
284488
 
284427
284489
  // src/cli/upgrade.ts
284428
284490
  init_cjs_shims();
284429
- var import_picocolors18 = __toESM(require_picocolors(), 1);
284491
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
284430
284492
  var import_ts_morph = __toESM(require_ts_morph(), 1);
284431
284493
  var DEFAULT_GLOBS = ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}", "ablo/**/*.{ts,tsx}", "lib/**/*.{ts,tsx}"];
284432
284494
  var VERB_ARGS = {
@@ -284504,7 +284566,7 @@ async function upgrade(argv) {
284504
284566
  project.addSourceFilesAtPaths(globs.length > 0 ? globs : DEFAULT_GLOBS);
284505
284567
  const files = project.getSourceFiles();
284506
284568
  if (files.length === 0) {
284507
- console.log(import_picocolors18.default.yellow(' No .ts/.tsx files found. Pass a glob, e.g. `ablo upgrade "src/**/*.tsx"`.'));
284569
+ console.log(import_picocolors19.default.yellow(' No .ts/.tsx files found. Pass a glob, e.g. `ablo upgrade "src/**/*.tsx"`.'));
284508
284570
  return;
284509
284571
  }
284510
284572
  const edits = [];
@@ -284578,39 +284640,39 @@ async function upgrade(argv) {
284578
284640
  const rel = (f) => f.replace(cwd + "/", "");
284579
284641
  console.log();
284580
284642
  if (edits.length === 0 && manual.length === 0) {
284581
- console.log(import_picocolors18.default.green(" \u2713 Nothing to migrate \u2014 your code is already on the current API."));
284643
+ console.log(import_picocolors19.default.green(" \u2713 Nothing to migrate \u2014 your code is already on the current API."));
284582
284644
  return;
284583
284645
  }
284584
284646
  if (edits.length > 0) {
284585
- console.log(import_picocolors18.default.bold(` ${write ? "Applied" : "Would apply"} ${edits.length} change${edits.length === 1 ? "" : "s"}:`));
284647
+ console.log(import_picocolors19.default.bold(` ${write ? "Applied" : "Would apply"} ${edits.length} change${edits.length === 1 ? "" : "s"}:`));
284586
284648
  for (const e2 of edits) {
284587
- console.log(` ${import_picocolors18.default.dim(`${rel(e2.file)}:${e2.line}`)} ${import_picocolors18.default.cyan(e2.rule)}`);
284588
- console.log(` ${import_picocolors18.default.red("-")} ${e2.before}`);
284589
- console.log(` ${import_picocolors18.default.green("+")} ${e2.after}`);
284649
+ console.log(` ${import_picocolors19.default.dim(`${rel(e2.file)}:${e2.line}`)} ${import_picocolors19.default.cyan(e2.rule)}`);
284650
+ console.log(` ${import_picocolors19.default.red("-")} ${e2.before}`);
284651
+ console.log(` ${import_picocolors19.default.green("+")} ${e2.after}`);
284590
284652
  }
284591
284653
  }
284592
284654
  if (manual.length > 0) {
284593
284655
  console.log();
284594
- console.log(import_picocolors18.default.bold(import_picocolors18.default.yellow(` ${manual.length} spot${manual.length === 1 ? "" : "s"} need manual review (structural):`)));
284656
+ console.log(import_picocolors19.default.bold(import_picocolors19.default.yellow(` ${manual.length} spot${manual.length === 1 ? "" : "s"} need manual review (structural):`)));
284595
284657
  for (const m2 of manual) {
284596
- console.log(` ${import_picocolors18.default.dim(`${rel(m2.file)}:${m2.line}`)} ${import_picocolors18.default.yellow(m2.rule)}`);
284597
- console.log(` ${import_picocolors18.default.dim(m2.snippet)}`);
284658
+ console.log(` ${import_picocolors19.default.dim(`${rel(m2.file)}:${m2.line}`)} ${import_picocolors19.default.yellow(m2.rule)}`);
284659
+ console.log(` ${import_picocolors19.default.dim(m2.snippet)}`);
284598
284660
  console.log(` \u2192 ${m2.hint}`);
284599
284661
  }
284600
284662
  }
284601
284663
  console.log();
284602
284664
  if (write) {
284603
284665
  await project.save();
284604
- console.log(import_picocolors18.default.green(` \u2713 Wrote ${edits.length} change${edits.length === 1 ? "" : "s"}. Review the diff, run your typecheck.`));
284666
+ console.log(import_picocolors19.default.green(` \u2713 Wrote ${edits.length} change${edits.length === 1 ? "" : "s"}. Review the diff, run your typecheck.`));
284605
284667
  } else {
284606
- console.log(import_picocolors18.default.dim(" Dry run. Re-run with `--write` to apply the auto-fixes above (manual items are never auto-written)."));
284668
+ console.log(import_picocolors19.default.dim(" Dry run. Re-run with `--write` to apply the auto-fixes above (manual items are never auto-written)."));
284607
284669
  }
284608
284670
  }
284609
284671
 
284610
284672
  // src/cli/pull.ts
284611
284673
  init_cjs_shims();
284612
284674
  init_errors();
284613
- var import_picocolors19 = __toESM(require_picocolors(), 1);
284675
+ var import_picocolors20 = __toESM(require_picocolors(), 1);
284614
284676
  init_src();
284615
284677
  var import_fs9 = require("fs");
284616
284678
  init_theme();
@@ -284724,45 +284786,45 @@ async function pull(argv) {
284724
284786
  try {
284725
284787
  args = parsePullArgs(argv);
284726
284788
  } catch (err) {
284727
- console.error(import_picocolors19.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284789
+ console.error(import_picocolors20.default.red(` ${err instanceof Error ? err.message : String(err)}`));
284728
284790
  process.exit(1);
284729
284791
  }
284730
284792
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
284731
284793
  if (!dbUrl) {
284732
- console.error(import_picocolors19.default.red(` No database.`) + import_picocolors19.default.dim(` Set ${import_picocolors19.default.bold("DATABASE_URL")} to the Postgres to pull from.`));
284794
+ console.error(import_picocolors20.default.red(` No database.`) + import_picocolors20.default.dim(` Set ${import_picocolors20.default.bold("DATABASE_URL")} to the Postgres to pull from.`));
284733
284795
  process.exit(1);
284734
284796
  }
284735
284797
  if ((0, import_fs9.existsSync)(args.out) && !args.force) {
284736
284798
  console.error(
284737
- import_picocolors19.default.red(` ${args.out} already exists.`) + import_picocolors19.default.dim(` Re-run with ${import_picocolors19.default.bold("--force")} to overwrite.`)
284799
+ import_picocolors20.default.red(` ${args.out} already exists.`) + import_picocolors20.default.dim(` Re-run with ${import_picocolors20.default.bold("--force")} to overwrite.`)
284738
284800
  );
284739
284801
  process.exit(1);
284740
284802
  }
284741
284803
  console.log(`
284742
- ${brand("ablo")} ${import_picocolors19.default.dim("pull")} ${import_picocolors19.default.dim(`schema "${args.appSchema}"`)}
284804
+ ${brand("ablo")} ${import_picocolors20.default.dim("pull")} ${import_picocolors20.default.dim(`schema "${args.appSchema}"`)}
284743
284805
  `);
284744
284806
  let result;
284745
284807
  try {
284746
284808
  result = await buildSchemaSourceFromDb({ dbUrl, appSchema: args.appSchema, importPath: args.importPath });
284747
284809
  } catch (err) {
284748
- console.error(import_picocolors19.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
284810
+ console.error(import_picocolors20.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
284749
284811
  process.exit(1);
284750
284812
  }
284751
284813
  if (result.models.length === 0) {
284752
284814
  console.error(
284753
- import_picocolors19.default.yellow(` No adoptable tables found`) + import_picocolors19.default.dim(` (a model needs an ${import_picocolors19.default.bold("id")} + ${import_picocolors19.default.bold("organization_id")} column).`)
284815
+ import_picocolors20.default.yellow(` No adoptable tables found`) + import_picocolors20.default.dim(` (a model needs an ${import_picocolors20.default.bold("id")} + ${import_picocolors20.default.bold("organization_id")} column).`)
284754
284816
  );
284755
284817
  process.exit(1);
284756
284818
  }
284757
284819
  (0, import_fs9.writeFileSync)(args.out, result.source);
284758
- console.log(` ${import_picocolors19.default.green("\u2713")} wrote ${import_picocolors19.default.bold(args.out)} ${import_picocolors19.default.dim(`(${result.models.length} models)`)}`);
284759
- console.log(` ${import_picocolors19.default.dim(`models: ${result.models.join(", ")}`)}`);
284820
+ console.log(` ${import_picocolors20.default.green("\u2713")} wrote ${import_picocolors20.default.bold(args.out)} ${import_picocolors20.default.dim(`(${result.models.length} models)`)}`);
284821
+ console.log(` ${import_picocolors20.default.dim(`models: ${result.models.join(", ")}`)}`);
284760
284822
  if (result.skipped > 0) {
284761
- console.log(` ${import_picocolors19.default.dim(`${result.skipped} table(s) skipped \u2014 no id/organization_id`)}`);
284823
+ console.log(` ${import_picocolors20.default.dim(`${result.skipped} table(s) skipped \u2014 no id/organization_id`)}`);
284762
284824
  }
284763
284825
  console.log(
284764
284826
  `
284765
- ${import_picocolors19.default.dim("Introspection is lossy (enums, JSON shape, relations). Review the file, then")} ${import_picocolors19.default.bold("ablo check")}.
284827
+ ${import_picocolors20.default.dim("Introspection is lossy (enums, JSON shape, relations). Review the file, then")} ${import_picocolors20.default.bold("ablo check")}.
284766
284828
  `
284767
284829
  );
284768
284830
  }
@@ -284770,7 +284832,7 @@ async function pull(argv) {
284770
284832
  // src/cli/prismaPull.ts
284771
284833
  init_cjs_shims();
284772
284834
  init_errors();
284773
- var import_picocolors20 = __toESM(require_picocolors(), 1);
284835
+ var import_picocolors21 = __toESM(require_picocolors(), 1);
284774
284836
  var import_fs10 = require("fs");
284775
284837
  init_theme();
284776
284838
 
@@ -285055,55 +285117,55 @@ async function prismaPull(argv) {
285055
285117
  try {
285056
285118
  args = parsePrismaPullArgs(argv);
285057
285119
  } catch (err) {
285058
- console.error(import_picocolors20.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285120
+ console.error(import_picocolors21.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285059
285121
  process.exit(1);
285060
285122
  }
285061
285123
  if (!(0, import_fs10.existsSync)(args.schema)) {
285062
285124
  console.error(
285063
- import_picocolors20.default.red(` No Prisma schema at ${import_picocolors20.default.bold(args.schema)}.`) + import_picocolors20.default.dim(` Pass a path: ${import_picocolors20.default.bold("ablo pull prisma <path>")}.`)
285125
+ import_picocolors21.default.red(` No Prisma schema at ${import_picocolors21.default.bold(args.schema)}.`) + import_picocolors21.default.dim(` Pass a path: ${import_picocolors21.default.bold("ablo pull prisma <path>")}.`)
285064
285126
  );
285065
285127
  process.exit(1);
285066
285128
  }
285067
285129
  if ((0, import_fs10.existsSync)(args.out) && !args.force) {
285068
285130
  console.error(
285069
- import_picocolors20.default.red(` ${args.out} already exists.`) + import_picocolors20.default.dim(` Re-run with ${import_picocolors20.default.bold("--force")} to overwrite.`)
285131
+ import_picocolors21.default.red(` ${args.out} already exists.`) + import_picocolors21.default.dim(` Re-run with ${import_picocolors21.default.bold("--force")} to overwrite.`)
285070
285132
  );
285071
285133
  process.exit(1);
285072
285134
  }
285073
285135
  console.log(`
285074
- ${brand("ablo")} ${import_picocolors20.default.dim("pull prisma")} ${import_picocolors20.default.dim(args.schema)}
285136
+ ${brand("ablo")} ${import_picocolors21.default.dim("pull prisma")} ${import_picocolors21.default.dim(args.schema)}
285075
285137
  `);
285076
285138
  let result;
285077
285139
  try {
285078
285140
  const src = (0, import_fs10.readFileSync)(args.schema, "utf8");
285079
285141
  result = buildSchemaSourceFromPrisma({ src, importPath: args.importPath });
285080
285142
  } catch (err) {
285081
- console.error(import_picocolors20.default.red(` Couldn't parse the schema: ${err instanceof Error ? err.message : String(err)}`));
285143
+ console.error(import_picocolors21.default.red(` Couldn't parse the schema: ${err instanceof Error ? err.message : String(err)}`));
285082
285144
  process.exit(1);
285083
285145
  }
285084
285146
  if (result.models.length === 0) {
285085
285147
  console.error(
285086
- import_picocolors20.default.yellow(` No adoptable models found`) + import_picocolors20.default.dim(` (a model needs an ${import_picocolors20.default.bold("id")} + ${import_picocolors20.default.bold("organizationId")} / ${import_picocolors20.default.bold("organization_id")}).`)
285148
+ import_picocolors21.default.yellow(` No adoptable models found`) + import_picocolors21.default.dim(` (a model needs an ${import_picocolors21.default.bold("id")} + ${import_picocolors21.default.bold("organizationId")} / ${import_picocolors21.default.bold("organization_id")}).`)
285087
285149
  );
285088
285150
  process.exit(1);
285089
285151
  }
285090
285152
  (0, import_fs10.writeFileSync)(args.out, result.source);
285091
- console.log(` ${import_picocolors20.default.green("\u2713")} wrote ${import_picocolors20.default.bold(args.out)} ${import_picocolors20.default.dim(`(${result.models.length} models)`)}`);
285092
- console.log(` ${import_picocolors20.default.dim(`models: ${result.models.join(", ")}`)}`);
285153
+ console.log(` ${import_picocolors21.default.green("\u2713")} wrote ${import_picocolors21.default.bold(args.out)} ${import_picocolors21.default.dim(`(${result.models.length} models)`)}`);
285154
+ console.log(` ${import_picocolors21.default.dim(`models: ${result.models.join(", ")}`)}`);
285093
285155
  if (result.skipped.length > 0) {
285094
- console.log(` ${import_picocolors20.default.dim(`${result.skipped.length} model(s) skipped:`)}`);
285095
- for (const s of result.skipped) console.log(` ${import_picocolors20.default.dim(`- ${s.name}: ${s.reason}`)}`);
285156
+ console.log(` ${import_picocolors21.default.dim(`${result.skipped.length} model(s) skipped:`)}`);
285157
+ for (const s of result.skipped) console.log(` ${import_picocolors21.default.dim(`- ${s.name}: ${s.reason}`)}`);
285096
285158
  }
285097
285159
  console.log(
285098
285160
  `
285099
- ${import_picocolors20.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors20.default.bold("ablo check")}.
285161
+ ${import_picocolors21.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors21.default.bold("ablo check")}.
285100
285162
  `
285101
285163
  );
285102
285164
  }
285103
285165
 
285104
285166
  // src/cli/drizzlePull.ts
285105
285167
  init_cjs_shims();
285106
- var import_picocolors21 = __toESM(require_picocolors(), 1);
285168
+ var import_picocolors22 = __toESM(require_picocolors(), 1);
285107
285169
  init_errors();
285108
285170
  var import_fs11 = require("fs");
285109
285171
  var import_path6 = require("path");
@@ -285242,27 +285304,27 @@ async function drizzlePull(argv) {
285242
285304
  try {
285243
285305
  args = parseDrizzlePullArgs(argv);
285244
285306
  } catch (err) {
285245
- console.error(import_picocolors21.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285307
+ console.error(import_picocolors22.default.red(` ${err instanceof Error ? err.message : String(err)}`));
285246
285308
  process.exit(1);
285247
285309
  }
285248
285310
  if (!args.schema) {
285249
285311
  console.error(
285250
- import_picocolors21.default.red(` No Drizzle schema given.`) + import_picocolors21.default.dim(` Pass the module: ${import_picocolors21.default.bold("ablo pull drizzle src/db/schema.ts")}.`)
285312
+ import_picocolors22.default.red(` No Drizzle schema given.`) + import_picocolors22.default.dim(` Pass the module: ${import_picocolors22.default.bold("ablo pull drizzle src/db/schema.ts")}.`)
285251
285313
  );
285252
285314
  process.exit(1);
285253
285315
  }
285254
285316
  if (!(0, import_fs11.existsSync)(args.schema)) {
285255
- console.error(import_picocolors21.default.red(` No file at ${import_picocolors21.default.bold(args.schema)}.`));
285317
+ console.error(import_picocolors22.default.red(` No file at ${import_picocolors22.default.bold(args.schema)}.`));
285256
285318
  process.exit(1);
285257
285319
  }
285258
285320
  if ((0, import_fs11.existsSync)(args.out) && !args.force) {
285259
285321
  console.error(
285260
- import_picocolors21.default.red(` ${args.out} already exists.`) + import_picocolors21.default.dim(` Re-run with ${import_picocolors21.default.bold("--force")} to overwrite.`)
285322
+ import_picocolors22.default.red(` ${args.out} already exists.`) + import_picocolors22.default.dim(` Re-run with ${import_picocolors22.default.bold("--force")} to overwrite.`)
285261
285323
  );
285262
285324
  process.exit(1);
285263
285325
  }
285264
285326
  console.log(`
285265
- ${brand("ablo")} ${import_picocolors21.default.dim("pull drizzle")} ${import_picocolors21.default.dim(args.schema)}
285327
+ ${brand("ablo")} ${import_picocolors22.default.dim("pull drizzle")} ${import_picocolors22.default.dim(args.schema)}
285266
285328
  `);
285267
285329
  let result;
285268
285330
  try {
@@ -285270,26 +285332,26 @@ async function drizzlePull(argv) {
285270
285332
  result = await buildSchemaSourceFromDrizzle({ mod, importPath: args.importPath });
285271
285333
  } catch (err) {
285272
285334
  const msg = err instanceof Error ? err.message : String(err);
285273
- const hint = msg.includes("Cannot find package 'drizzle-orm'") ? import_picocolors21.default.dim(` (install ${import_picocolors21.default.bold("drizzle-orm")} in this project)`) : "";
285274
- console.error(import_picocolors21.default.red(` Couldn't load the schema: ${msg}`) + hint);
285335
+ const hint = msg.includes("Cannot find package 'drizzle-orm'") ? import_picocolors22.default.dim(` (install ${import_picocolors22.default.bold("drizzle-orm")} in this project)`) : "";
285336
+ console.error(import_picocolors22.default.red(` Couldn't load the schema: ${msg}`) + hint);
285275
285337
  process.exit(1);
285276
285338
  }
285277
285339
  if (result.models.length === 0) {
285278
285340
  console.error(
285279
- import_picocolors21.default.yellow(` No adoptable tables found`) + import_picocolors21.default.dim(` (a table needs an ${import_picocolors21.default.bold("id")} + ${import_picocolors21.default.bold("organization_id")} column).`)
285341
+ import_picocolors22.default.yellow(` No adoptable tables found`) + import_picocolors22.default.dim(` (a table needs an ${import_picocolors22.default.bold("id")} + ${import_picocolors22.default.bold("organization_id")} column).`)
285280
285342
  );
285281
285343
  process.exit(1);
285282
285344
  }
285283
285345
  (0, import_fs11.writeFileSync)(args.out, result.source);
285284
- console.log(` ${import_picocolors21.default.green("\u2713")} wrote ${import_picocolors21.default.bold(args.out)} ${import_picocolors21.default.dim(`(${result.models.length} models)`)}`);
285285
- console.log(` ${import_picocolors21.default.dim(`models: ${result.models.join(", ")}`)}`);
285346
+ console.log(` ${import_picocolors22.default.green("\u2713")} wrote ${import_picocolors22.default.bold(args.out)} ${import_picocolors22.default.dim(`(${result.models.length} models)`)}`);
285347
+ console.log(` ${import_picocolors22.default.dim(`models: ${result.models.join(", ")}`)}`);
285286
285348
  if (result.skipped.length > 0) {
285287
- console.log(` ${import_picocolors21.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
285288
- for (const s of result.skipped) console.log(` ${import_picocolors21.default.dim(`- ${s.name}: ${s.reason}`)}`);
285349
+ console.log(` ${import_picocolors22.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
285350
+ for (const s of result.skipped) console.log(` ${import_picocolors22.default.dim(`- ${s.name}: ${s.reason}`)}`);
285289
285351
  }
285290
285352
  console.log(
285291
285353
  `
285292
- ${import_picocolors21.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors21.default.bold("ablo check")}.
285354
+ ${import_picocolors22.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors22.default.bold("ablo check")}.
285293
285355
  `
285294
285356
  );
285295
285357
  }
@@ -285299,7 +285361,7 @@ init_theme();
285299
285361
 
285300
285362
  // src/cli/renderError.ts
285301
285363
  init_cjs_shims();
285302
- var import_picocolors22 = __toESM(require_picocolors(), 1);
285364
+ var import_picocolors23 = __toESM(require_picocolors(), 1);
285303
285365
  init_errors();
285304
285366
  init_theme();
285305
285367
  var RECOVERY_HINT = {
@@ -285321,15 +285383,15 @@ function isStringArray(v2) {
285321
285383
  function renderKnownDetails(details, line) {
285322
285384
  if (!details) return;
285323
285385
  const { retryAfterSeconds, missingIds, requiredCapability, unexecutable, errors } = details;
285324
- if (typeof retryAfterSeconds === "number") line(` ${import_picocolors22.default.dim("retry")} after ${retryAfterSeconds}s`);
285386
+ if (typeof retryAfterSeconds === "number") line(` ${import_picocolors23.default.dim("retry")} after ${retryAfterSeconds}s`);
285325
285387
  if (isStringArray(missingIds) && missingIds.length > 0) {
285326
285388
  const shown = missingIds.slice(0, 5).join(", ");
285327
285389
  const more = missingIds.length > 5 ? ` (+${missingIds.length - 5} more)` : "";
285328
- line(` ${import_picocolors22.default.dim("missing")} ${shown}${more}`);
285390
+ line(` ${import_picocolors23.default.dim("missing")} ${shown}${more}`);
285329
285391
  }
285330
- if (typeof requiredCapability === "string") line(` ${import_picocolors22.default.dim("needs")} ${requiredCapability}`);
285392
+ if (typeof requiredCapability === "string") line(` ${import_picocolors23.default.dim("needs")} ${requiredCapability}`);
285331
285393
  if (Array.isArray(unexecutable) && unexecutable.length > 0) {
285332
- line(` ${import_picocolors22.default.dim("blocked")} ${unexecutable.length} change(s) can't be applied \u2014 see \`unexecutable\` (--verbose)`);
285394
+ line(` ${import_picocolors23.default.dim("blocked")} ${unexecutable.length} change(s) can't be applied \u2014 see \`unexecutable\` (--verbose)`);
285333
285395
  }
285334
285396
  if (Array.isArray(errors)) {
285335
285397
  for (const e2 of errors.slice(0, 8)) {
@@ -285337,7 +285399,7 @@ function renderKnownDetails(details, line) {
285337
285399
  const rec = e2;
285338
285400
  const where = typeof rec.param === "string" ? `${rec.param}: ` : "";
285339
285401
  const msg = typeof rec.message === "string" ? rec.message : "";
285340
- if (msg) line(` ${import_picocolors22.default.dim("\xB7")} ${where}${msg}`);
285402
+ if (msg) line(` ${import_picocolors23.default.dim("\xB7")} ${where}${msg}`);
285341
285403
  }
285342
285404
  }
285343
285405
  }
@@ -285348,22 +285410,22 @@ function renderCliError(err, opts = {}) {
285348
285410
  });
285349
285411
  const verbose = opts.verbose ?? (process.argv.includes("--verbose") || process.env.ABLO_VERBOSE === "1");
285350
285412
  if (err instanceof AbloError) {
285351
- const codeTag = err.code ? ` ${import_picocolors22.default.dim(`[${err.code}]`)}` : "";
285413
+ const codeTag = err.code ? ` ${import_picocolors23.default.dim(`[${err.code}]`)}` : "";
285352
285414
  line("");
285353
- line(` ${brand("ablo")} ${import_picocolors22.default.red("\u2717")} ${import_picocolors22.default.bold(titleForType(err.type))}${codeTag}`);
285415
+ line(` ${brand("ablo")} ${import_picocolors23.default.red("\u2717")} ${import_picocolors23.default.bold(titleForType(err.type))}${codeTag}`);
285354
285416
  line("");
285355
285417
  line(` ${err.message}`);
285356
- if (err.param) line(` ${import_picocolors22.default.dim("field")} ${err.param}`);
285418
+ if (err.param) line(` ${import_picocolors23.default.dim("field")} ${err.param}`);
285357
285419
  renderKnownDetails(err.details, line);
285358
285420
  const hint = err.code ? RECOVERY_HINT[classifyRecovery(err.code)] : void 0;
285359
- if (hint) line(` ${import_picocolors22.default.dim(hint)}`);
285360
- if (err.docUrl) line(` ${import_picocolors22.default.dim("docs")} ${err.docUrl}`);
285361
- if (err.requestId) line(` ${import_picocolors22.default.dim("ref")} ${err.requestId}`);
285421
+ if (hint) line(` ${import_picocolors23.default.dim(hint)}`);
285422
+ if (err.docUrl) line(` ${import_picocolors23.default.dim("docs")} ${err.docUrl}`);
285423
+ if (err.requestId) line(` ${import_picocolors23.default.dim("ref")} ${err.requestId}`);
285362
285424
  if (verbose) {
285363
285425
  if (err.details && Object.keys(err.details).length > 0) {
285364
- line(` ${import_picocolors22.default.dim("details")} ${JSON.stringify(err.details)}`);
285426
+ line(` ${import_picocolors23.default.dim("details")} ${JSON.stringify(err.details)}`);
285365
285427
  }
285366
- if (err.stack) line(import_picocolors22.default.dim(err.stack));
285428
+ if (err.stack) line(import_picocolors23.default.dim(err.stack));
285367
285429
  }
285368
285430
  line("");
285369
285431
  process.exitCode = 1;
@@ -285371,16 +285433,16 @@ function renderCliError(err, opts = {}) {
285371
285433
  }
285372
285434
  const message = err instanceof Error ? err.message : String(err);
285373
285435
  line("");
285374
- line(` ${brand("ablo")} ${import_picocolors22.default.red("\u2717")} ${message}`);
285375
- if (verbose && err instanceof Error && err.stack) line(import_picocolors22.default.dim(err.stack));
285376
- else line(` ${import_picocolors22.default.dim("Run with --verbose for the full error.")}`);
285436
+ line(` ${brand("ablo")} ${import_picocolors23.default.red("\u2717")} ${message}`);
285437
+ if (verbose && err instanceof Error && err.stack) line(import_picocolors23.default.dim(err.stack));
285438
+ else line(` ${import_picocolors23.default.dim("Run with --verbose for the full error.")}`);
285377
285439
  line("");
285378
285440
  process.exitCode = 1;
285379
285441
  }
285380
285442
 
285381
285443
  // src/cli/index.ts
285382
285444
  var LOGO = `
285383
- ${brand("ablo")} ${import_picocolors23.default.dim("sync engine")}
285445
+ ${brand("ablo")} ${import_picocolors24.default.dim("sync engine")}
285384
285446
  `;
285385
285447
  var SUBCOMMAND_USAGE = {
285386
285448
  connect: CONNECT_USAGE,
@@ -285415,7 +285477,7 @@ async function main() {
285415
285477
  const devArgs = process.argv.slice(3);
285416
285478
  const oneShot = devArgs.includes("--no-watch");
285417
285479
  console.log(
285418
- import_picocolors23.default.dim(
285480
+ import_picocolors24.default.dim(
285419
285481
  oneShot ? " `ablo dev --no-watch` is `ablo push` (push once, no watcher) \u2014 running that." : " `ablo dev` is now `ablo push --watch` \u2014 running that."
285420
285482
  )
285421
285483
  );
@@ -285444,14 +285506,14 @@ async function main() {
285444
285506
  const guard = guardActiveProjectKey();
285445
285507
  if (!guard.ok && guard.available.length > 0 && !rest.includes("--url")) {
285446
285508
  console.error(
285447
- ` ${import_picocolors23.default.yellow("\u26A0")} active project ${import_picocolors23.default.bold(guard.activeProfile)} has no stored key ${import_picocolors23.default.dim(
285509
+ ` ${import_picocolors24.default.yellow("\u26A0")} active project ${import_picocolors24.default.bold(guard.activeProfile)} has no stored key ${import_picocolors24.default.dim(
285448
285510
  `(you have keys for: ${guard.available.join(", ")})`
285449
285511
  )}`
285450
285512
  );
285451
285513
  const loginCmd = guard.activeProfile === "default" ? "ablo login" : `ablo login --project ${guard.activeProfile}`;
285452
285514
  console.error(
285453
- import_picocolors23.default.dim(
285454
- ` Mint one with ${import_picocolors23.default.bold(loginCmd)}, or switch with ${import_picocolors23.default.bold("ablo projects use <slug>")}.`
285515
+ import_picocolors24.default.dim(
285516
+ ` Mint one with ${import_picocolors24.default.bold(loginCmd)}, or switch with ${import_picocolors24.default.bold("ablo projects use <slug>")}.`
285455
285517
  )
285456
285518
  );
285457
285519
  process.exitCode = 1;
@@ -285469,13 +285531,13 @@ async function main() {
285469
285531
  await generate(process.argv.slice(3));
285470
285532
  } else if (command === "schema") {
285471
285533
  console.error(
285472
- ` ${import_picocolors23.default.red("\u2717")} \`ablo schema push\` was renamed to \`${brand("ablo push")}\`.`
285534
+ ` ${import_picocolors24.default.red("\u2717")} \`ablo schema push\` was renamed to \`${brand("ablo push")}\`.`
285473
285535
  );
285474
285536
  console.error(` Run \`ablo push${process.argv.slice(4).join(" ") ? " " + process.argv.slice(4).join(" ") : ""}\` instead.`);
285475
285537
  process.exitCode = 1;
285476
285538
  } else {
285477
285539
  console.log(LOGO);
285478
- console.log(` ${import_picocolors23.default.bold("Usage:")}`);
285540
+ console.log(` ${import_picocolors24.default.bold("Usage:")}`);
285479
285541
  console.log(` npx ablo init Scaffold ablo/ directory + starter schema`);
285480
285542
  console.log(` npx ablo init --yes [--framework nextjs] Non-interactive (agents/CI): no prompts, flag-driven`);
285481
285543
  console.log(` [--auth apikey] [--storage replication|endpoint] [--project <slug>] [--no-project]`);
@@ -285512,10 +285574,10 @@ async function main() {
285512
285574
  console.log(` npx ablo generate Emit TypeScript types from your schema`);
285513
285575
  console.log(` npx ablo generate --out path.ts Write generated types to a path`);
285514
285576
  console.log();
285515
- console.log(` ${import_picocolors23.default.bold("Schema workflow:")}`);
285577
+ console.log(` ${import_picocolors24.default.bold("Schema workflow:")}`);
285516
285578
  console.log(` The server holds its own copy of your schema \u2014 edit ${brand("ablo/schema.ts")}, then`);
285517
285579
  console.log(` run ${brand("ablo push")} (or keep ${brand("ablo dev")} running) before the server will accept`);
285518
- console.log(` writes to new or changed models. Skip it and writes fail with ${import_picocolors23.default.yellow("server_execute_unknown_model")}.`);
285580
+ console.log(` writes to new or changed models. Skip it and writes fail with ${import_picocolors24.default.yellow("server_execute_unknown_model")}.`);
285519
285581
  console.log();
285520
285582
  }
285521
285583
  }
@@ -285566,7 +285628,7 @@ async function ensureInitProject(opts) {
285566
285628
  const ensured = await ensureProject(slug);
285567
285629
  if (ensured) {
285568
285630
  console.log(
285569
- ` ${import_picocolors23.default.green("\u2713")} ${ensured.created ? "Created" : "Using"} project ${import_picocolors23.default.bold(ensured.slug)} ${import_picocolors23.default.dim(`(${ensured.id})`)} \u2014 keys you mint for it are isolated from the org's other apps.`
285631
+ ` ${import_picocolors24.default.green("\u2713")} ${ensured.created ? "Created" : "Using"} project ${import_picocolors24.default.bold(ensured.slug)} ${import_picocolors24.default.dim(`(${ensured.id})`)} \u2014 keys you mint for it are isolated from the org's other apps.`
285570
285632
  );
285571
285633
  }
285572
285634
  }
@@ -285608,7 +285670,7 @@ async function chooseBool(flagValue, fallback, interactive, prompt) {
285608
285670
  async function init(args = []) {
285609
285671
  const opts = parseInitArgs(args);
285610
285672
  const interactive = Boolean(process.stdin.isTTY) && !opts.yes && !process.env.CI;
285611
- Ie(`${brand("ablo")} ${import_picocolors23.default.dim("sync engine")}`);
285673
+ Ie(`${brand("ablo")} ${import_picocolors24.default.dim("sync engine")}`);
285612
285674
  if (!(0, import_fs12.existsSync)("package.json")) {
285613
285675
  xe("No package.json found. Run this from your project root.");
285614
285676
  process.exit(1);
@@ -285686,7 +285748,7 @@ async function init(args = []) {
285686
285748
  if (pullExisting) {
285687
285749
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
285688
285750
  if (!dbUrl) {
285689
- schemaNote = import_picocolors23.default.dim(" (no DATABASE_URL \u2014 wrote starter; run `ablo pull` later)");
285751
+ schemaNote = import_picocolors24.default.dim(" (no DATABASE_URL \u2014 wrote starter; run `ablo pull` later)");
285690
285752
  } else {
285691
285753
  try {
285692
285754
  const pulled = await buildSchemaSourceFromDb({
@@ -285696,12 +285758,12 @@ async function init(args = []) {
285696
285758
  });
285697
285759
  if (pulled.models.length > 0) {
285698
285760
  schemaSource = pulled.source;
285699
- schemaNote = import_picocolors23.default.dim(` (pulled ${pulled.models.length} models)`);
285761
+ schemaNote = import_picocolors24.default.dim(` (pulled ${pulled.models.length} models)`);
285700
285762
  } else {
285701
- schemaNote = import_picocolors23.default.dim(" (no adoptable tables \u2014 wrote starter)");
285763
+ schemaNote = import_picocolors24.default.dim(" (no adoptable tables \u2014 wrote starter)");
285702
285764
  }
285703
285765
  } catch {
285704
- schemaNote = import_picocolors23.default.dim(" (pull failed \u2014 wrote starter)");
285766
+ schemaNote = import_picocolors24.default.dim(" (pull failed \u2014 wrote starter)");
285705
285767
  }
285706
285768
  }
285707
285769
  }
@@ -285727,14 +285789,14 @@ async function init(args = []) {
285727
285789
  const existing = (0, import_fs12.readFileSync)(envFile, "utf-8");
285728
285790
  if (!existing.includes("ABLO_")) {
285729
285791
  (0, import_fs12.writeFileSync)(envFile, existing + "\n" + envBody);
285730
- created.push(`${envFile} ${import_picocolors23.default.dim("(appended)")}`);
285792
+ created.push(`${envFile} ${import_picocolors24.default.dim("(appended)")}`);
285731
285793
  } else {
285732
- created.push(`${envFile} ${import_picocolors23.default.dim("(already configured)")}`);
285794
+ created.push(`${envFile} ${import_picocolors24.default.dim("(already configured)")}`);
285733
285795
  }
285734
285796
  }
285735
285797
  if (wireRealKey && resolvedKey) {
285736
285798
  wireEnvLocal(resolvedKey);
285737
- created.push(`.env.local ${import_picocolors23.default.dim("(ABLO_API_KEY set from your login)")}`);
285799
+ created.push(`.env.local ${import_picocolors24.default.dim("(ABLO_API_KEY set from your login)")}`);
285738
285800
  }
285739
285801
  if (agent) {
285740
285802
  (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "agent.ts"), generateAgent());
@@ -285749,17 +285811,17 @@ async function init(args = []) {
285749
285811
  }
285750
285812
  const providersPath = (0, import_path7.join)(layout.appBase, "providers.tsx");
285751
285813
  (0, import_fs12.writeFileSync)(providersPath, generateProviders());
285752
- created.push(`${providersPath} ${import_picocolors23.default.dim(`(wrap ${(0, import_path7.join)(layout.appBase, "layout.tsx")} in <Providers>)`)}`);
285814
+ created.push(`${providersPath} ${import_picocolors24.default.dim(`(wrap ${(0, import_path7.join)(layout.appBase, "layout.tsx")} in <Providers>)`)}`);
285753
285815
  const sessionDir = (0, import_path7.join)(layout.appBase, "api", "ablo-session");
285754
285816
  (0, import_fs12.mkdirSync)(sessionDir, { recursive: true });
285755
285817
  (0, import_fs12.writeFileSync)((0, import_path7.join)(sessionDir, "route.ts"), generateSessionRoute());
285756
- created.push(`${(0, import_path7.join)(sessionDir, "route.ts")} ${import_picocolors23.default.dim("(wire your auth)")}`);
285818
+ created.push(`${(0, import_path7.join)(sessionDir, "route.ts")} ${import_picocolors24.default.dim("(wire your auth)")}`);
285757
285819
  }
285758
285820
  if (framework !== "vanilla") {
285759
285821
  (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "TaskList.tsx"), generateComponent());
285760
285822
  created.push(`${abloDir}/TaskList.tsx`);
285761
285823
  }
285762
- Me(created.map((f) => `${import_picocolors23.default.green("\u2713")} ${f}`).join("\n"), "Created");
285824
+ Me(created.map((f) => `${import_picocolors24.default.green("\u2713")} ${f}`).join("\n"), "Created");
285763
285825
  const pm = detectPackageManager();
285764
285826
  if (opts.install) {
285765
285827
  const s = Y2();
@@ -285768,47 +285830,47 @@ async function init(args = []) {
285768
285830
  (0, import_child_process3.execSync)(`${pm} add @abloatai/ablo`, { stdio: "ignore" });
285769
285831
  s.stop("Installed @abloatai/ablo");
285770
285832
  } catch {
285771
- s.stop(`${import_picocolors23.default.yellow("!")} Couldn't auto-install \u2014 run ${import_picocolors23.default.bold(`${pm} install @abloatai/ablo`)}`);
285833
+ s.stop(`${import_picocolors24.default.yellow("!")} Couldn't auto-install \u2014 run ${import_picocolors24.default.bold(`${pm} install @abloatai/ablo`)}`);
285772
285834
  }
285773
285835
  }
285774
285836
  const steps = [
285775
- `Get a ${import_picocolors23.default.bold("sk_test_")} key at ${import_picocolors23.default.cyan("https://abloatai.com")}`,
285776
- `Run ${import_picocolors23.default.bold("npx ablo login")} (or add ${import_picocolors23.default.bold("ABLO_API_KEY")} to ${import_picocolors23.default.bold(envFile)})`,
285777
- `Set ${import_picocolors23.default.bold("DATABASE_URL")} in ${import_picocolors23.default.bold(envFile)} \u2014 your Postgres is the system of record; rows live there, never with Ablo`,
285778
- `Run ${import_picocolors23.default.bold("npx ablo dev")} \u2014 pushes your schema definition and watches for changes`,
285837
+ `Get a ${import_picocolors24.default.bold("sk_test_")} key at ${import_picocolors24.default.cyan("https://abloatai.com")}`,
285838
+ `Run ${import_picocolors24.default.bold("npx ablo login")} (or add ${import_picocolors24.default.bold("ABLO_API_KEY")} to ${import_picocolors24.default.bold(envFile)})`,
285839
+ `Set ${import_picocolors24.default.bold("DATABASE_URL")} in ${import_picocolors24.default.bold(envFile)} \u2014 your Postgres is the system of record; rows live there, never with Ablo`,
285840
+ `Run ${import_picocolors24.default.bold("npx ablo dev")} \u2014 pushes your schema definition and watches for changes`,
285779
285841
  ...storage === "replication" ? [
285780
- `Connect your database \u2014 ${import_picocolors23.default.bold("npx ablo connect")} prints the one-time logical-replication setup SQL to run on your Postgres`,
285781
- `Verify it \u2014 ${import_picocolors23.default.bold("npx ablo connect check")} walks wal_level, the publication, the role, and replica identity, with the exact fix for anything missing`,
285782
- `Register it \u2014 ${import_picocolors23.default.bold("npx ablo connect register")} tells Ablo to start replicating; your app keeps writing through your own backend while Ablo tails the WAL`
285842
+ `Connect your database \u2014 ${import_picocolors24.default.bold("npx ablo connect")} prints the one-time logical-replication setup SQL to run on your Postgres`,
285843
+ `Verify it \u2014 ${import_picocolors24.default.bold("npx ablo connect check")} walks wal_level, the publication, the role, and replica identity, with the exact fix for anything missing`,
285844
+ `Register it \u2014 ${import_picocolors24.default.bold("npx ablo connect register")} tells Ablo to start replicating; your app keeps writing through your own backend while Ablo tails the WAL`
285783
285845
  ] : [
285784
- `Provision your DB: ${import_picocolors23.default.bold("npx ablo migrate")} (creates your Ablo-model tables + the adapter tables; keep your own migrations for everything else), then mount ${import_picocolors23.default.bold(`${abloDir}/data-source.ts`)} at ${import_picocolors23.default.bold("/api/ablo/source")}`
285846
+ `Provision your DB: ${import_picocolors24.default.bold("npx ablo migrate")} (creates your Ablo-model tables + the adapter tables; keep your own migrations for everything else), then mount ${import_picocolors24.default.bold(`${abloDir}/data-source.ts`)} at ${import_picocolors24.default.bold("/api/ablo/source")}`
285785
285847
  ],
285786
285848
  ...framework === "nextjs" ? [
285787
- `Wrap ${import_picocolors23.default.bold((0, import_path7.join)(layout.appBase, "layout.tsx"))} in ${import_picocolors23.default.bold("<Providers>")} (${(0, import_path7.join)(layout.appBase, "providers.tsx")}) and add your auth to ${import_picocolors23.default.bold((0, import_path7.join)(layout.appBase, "api", "ablo-session", "route.ts"))}`
285849
+ `Wrap ${import_picocolors24.default.bold((0, import_path7.join)(layout.appBase, "layout.tsx"))} in ${import_picocolors24.default.bold("<Providers>")} (${(0, import_path7.join)(layout.appBase, "providers.tsx")}) and add your auth to ${import_picocolors24.default.bold((0, import_path7.join)(layout.appBase, "api", "ablo-session", "route.ts"))}`
285788
285850
  ] : [],
285789
- `Run ${import_picocolors23.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
285851
+ `Run ${import_picocolors24.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
285790
285852
  ...agent ? [
285791
- `Run ${import_picocolors23.default.bold(`npx tsx ${abloDir}/agent.ts`)} \u2014 an AI teammate edits the same tasks`,
285792
- `Run ${import_picocolors23.default.bold("npx ablo logs")} to watch human + agent commits stream by`
285853
+ `Run ${import_picocolors24.default.bold(`npx tsx ${abloDir}/agent.ts`)} \u2014 an AI teammate edits the same tasks`,
285854
+ `Run ${import_picocolors24.default.bold("npx ablo logs")} to watch human + agent commits stream by`
285793
285855
  ] : []
285794
285856
  ];
285795
285857
  Me(steps.map((s, i) => `${i + 1}. ${s}`).join("\n"), "Next steps");
285796
285858
  const existingKey = resolveApiKey("sandbox");
285797
285859
  if (existingKey) {
285798
285860
  await ensureInitProject(opts);
285799
- Se(`Already authorized ${import_picocolors23.default.dim(`(${existingKey.slice(0, 11)}\u2026)`)} \u2014 run ${import_picocolors23.default.bold("npx ablo push")} next. ${import_picocolors23.default.dim("Docs:")} https://abloatai.com/docs`);
285861
+ Se(`Already authorized ${import_picocolors24.default.dim(`(${existingKey.slice(0, 11)}\u2026)`)} \u2014 run ${import_picocolors24.default.bold("npx ablo push")} next. ${import_picocolors24.default.dim("Docs:")} https://abloatai.com/docs`);
285800
285862
  return;
285801
285863
  }
285802
285864
  if (interactive && opts.login) {
285803
285865
  const loginNow = await ye({ message: "Log in now? (opens your browser)", initialValue: true });
285804
285866
  if (!pD(loginNow) && loginNow) {
285805
- Se(`${import_picocolors23.default.dim("Docs:")} https://abloatai.com/docs`);
285867
+ Se(`${import_picocolors24.default.dim("Docs:")} https://abloatai.com/docs`);
285806
285868
  await login();
285807
285869
  await ensureInitProject(opts);
285808
285870
  return;
285809
285871
  }
285810
285872
  }
285811
- Se(`Run ${import_picocolors23.default.bold("npx ablo login")} when ready. ${import_picocolors23.default.dim("Docs:")} https://abloatai.com/docs`);
285873
+ Se(`Run ${import_picocolors24.default.bold("npx ablo login")} when ready. ${import_picocolors24.default.dim("Docs:")} https://abloatai.com/docs`);
285812
285874
  }
285813
285875
  function generateSchema() {
285814
285876
  return `import { defineSchema, model, relation, z } from '@abloatai/ablo/schema';