@abloatai/ablo 0.9.4 → 0.9.6

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
@@ -125060,7 +125060,7 @@ ${lanes.join("\n")}
125060
125060
  }
125061
125061
  }
125062
125062
  function createImportCallExpressionAMD(arg, containsLexicalThis) {
125063
- const resolve5 = factory2.createUniqueName("resolve");
125063
+ const resolve6 = factory2.createUniqueName("resolve");
125064
125064
  const reject = factory2.createUniqueName("reject");
125065
125065
  const parameters = [
125066
125066
  factory2.createParameterDeclaration(
@@ -125069,7 +125069,7 @@ ${lanes.join("\n")}
125069
125069
  /*dotDotDotToken*/
125070
125070
  void 0,
125071
125071
  /*name*/
125072
- resolve5
125072
+ resolve6
125073
125073
  ),
125074
125074
  factory2.createParameterDeclaration(
125075
125075
  /*modifiers*/
@@ -125086,7 +125086,7 @@ ${lanes.join("\n")}
125086
125086
  factory2.createIdentifier("require"),
125087
125087
  /*typeArguments*/
125088
125088
  void 0,
125089
- [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve5, reject]
125089
+ [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve6, reject]
125090
125090
  )
125091
125091
  )
125092
125092
  ]);
@@ -211695,8 +211695,8 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
211695
211695
  installPackage(options) {
211696
211696
  this.packageInstallId++;
211697
211697
  const request = { kind: "installPackage", ...options, id: this.packageInstallId };
211698
- const promise = new Promise((resolve5, reject) => {
211699
- (this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map())).set(this.packageInstallId, { resolve: resolve5, reject });
211698
+ const promise = new Promise((resolve6, reject) => {
211699
+ (this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map())).set(this.packageInstallId, { resolve: resolve6, reject });
211700
211700
  });
211701
211701
  this.installer.send(request);
211702
211702
  return promise;
@@ -213965,7 +213965,7 @@ var require_path_browserify = __commonJS({
213965
213965
  }
213966
213966
  var posix = {
213967
213967
  // path.resolve([from ...], to)
213968
- resolve: function resolve5() {
213968
+ resolve: function resolve6() {
213969
213969
  var resolvedPath = "";
213970
213970
  var resolvedAbsolute = false;
213971
213971
  var cwd;
@@ -218865,41 +218865,41 @@ var require_queue = __commonJS({
218865
218865
  queue.drained = drained;
218866
218866
  return queue;
218867
218867
  function push2(value) {
218868
- var p2 = new Promise(function(resolve5, reject) {
218868
+ var p2 = new Promise(function(resolve6, reject) {
218869
218869
  pushCb(value, function(err, result) {
218870
218870
  if (err) {
218871
218871
  reject(err);
218872
218872
  return;
218873
218873
  }
218874
- resolve5(result);
218874
+ resolve6(result);
218875
218875
  });
218876
218876
  });
218877
218877
  p2.catch(noop3);
218878
218878
  return p2;
218879
218879
  }
218880
218880
  function unshift(value) {
218881
- var p2 = new Promise(function(resolve5, reject) {
218881
+ var p2 = new Promise(function(resolve6, reject) {
218882
218882
  unshiftCb(value, function(err, result) {
218883
218883
  if (err) {
218884
218884
  reject(err);
218885
218885
  return;
218886
218886
  }
218887
- resolve5(result);
218887
+ resolve6(result);
218888
218888
  });
218889
218889
  });
218890
218890
  p2.catch(noop3);
218891
218891
  return p2;
218892
218892
  }
218893
218893
  function drained() {
218894
- var p2 = new Promise(function(resolve5) {
218894
+ var p2 = new Promise(function(resolve6) {
218895
218895
  process.nextTick(function() {
218896
218896
  if (queue.idle()) {
218897
- resolve5();
218897
+ resolve6();
218898
218898
  } else {
218899
218899
  var previousDrain = queue.drain;
218900
218900
  queue.drain = function() {
218901
218901
  if (typeof previousDrain === "function") previousDrain();
218902
- resolve5();
218902
+ resolve6();
218903
218903
  queue.drain = previousDrain;
218904
218904
  };
218905
218905
  }
@@ -219396,9 +219396,9 @@ var require_stream3 = __commonJS({
219396
219396
  });
219397
219397
  }
219398
219398
  _getStat(filepath) {
219399
- return new Promise((resolve5, reject) => {
219399
+ return new Promise((resolve6, reject) => {
219400
219400
  this._stat(filepath, this._fsStatSettings, (error, stats) => {
219401
- return error === null ? resolve5(stats) : reject(error);
219401
+ return error === null ? resolve6(stats) : reject(error);
219402
219402
  });
219403
219403
  });
219404
219404
  }
@@ -219423,10 +219423,10 @@ var require_async5 = __commonJS({
219423
219423
  this._readerStream = new stream_1.default(this._settings);
219424
219424
  }
219425
219425
  dynamic(root, options) {
219426
- return new Promise((resolve5, reject) => {
219426
+ return new Promise((resolve6, reject) => {
219427
219427
  this._walkAsync(root, options, (error, entries) => {
219428
219428
  if (error === null) {
219429
- resolve5(entries);
219429
+ resolve6(entries);
219430
219430
  } else {
219431
219431
  reject(error);
219432
219432
  }
@@ -219436,10 +219436,10 @@ var require_async5 = __commonJS({
219436
219436
  async static(patterns, options) {
219437
219437
  const entries = [];
219438
219438
  const stream = this._readerStream.static(patterns, options);
219439
- return new Promise((resolve5, reject) => {
219439
+ return new Promise((resolve6, reject) => {
219440
219440
  stream.once("error", reject);
219441
219441
  stream.on("data", (entry) => entries.push(entry));
219442
- stream.once("end", () => resolve5(entries));
219442
+ stream.once("end", () => resolve6(entries));
219443
219443
  });
219444
219444
  }
219445
219445
  };
@@ -252345,12 +252345,12 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
252345
252345
  };
252346
252346
  var NodeRuntimeFileSystem = class {
252347
252347
  delete(path2) {
252348
- return new Promise((resolve5, reject) => {
252348
+ return new Promise((resolve6, reject) => {
252349
252349
  fs__namespace.rm(path2, { recursive: true }, (err) => {
252350
252350
  if (err)
252351
252351
  reject(err);
252352
252352
  else
252353
- resolve5();
252353
+ resolve6();
252354
252354
  });
252355
252355
  });
252356
252356
  }
@@ -252369,12 +252369,12 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
252369
252369
  }));
252370
252370
  }
252371
252371
  readFile(filePath, encoding = "utf-8") {
252372
- return new Promise((resolve5, reject) => {
252372
+ return new Promise((resolve6, reject) => {
252373
252373
  fs__namespace.readFile(filePath, encoding, (err, data) => {
252374
252374
  if (err)
252375
252375
  reject(err);
252376
252376
  else
252377
- resolve5(data);
252377
+ resolve6(data);
252378
252378
  });
252379
252379
  });
252380
252380
  }
@@ -252382,12 +252382,12 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
252382
252382
  return fs__namespace.readFileSync(filePath, encoding);
252383
252383
  }
252384
252384
  async writeFile(filePath, fileText) {
252385
- await new Promise((resolve5, reject) => {
252385
+ await new Promise((resolve6, reject) => {
252386
252386
  fs__namespace.writeFile(filePath, fileText, (err) => {
252387
252387
  if (err)
252388
252388
  reject(err);
252389
252389
  else
252390
- resolve5();
252390
+ resolve6();
252391
252391
  });
252392
252392
  });
252393
252393
  }
@@ -252401,12 +252401,12 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
252401
252401
  fs__namespace.mkdirSync(dirPath, { recursive: true });
252402
252402
  }
252403
252403
  move(srcPath, destPath) {
252404
- return new Promise((resolve5, reject) => {
252404
+ return new Promise((resolve6, reject) => {
252405
252405
  fs__namespace.rename(srcPath, destPath, (err) => {
252406
252406
  if (err)
252407
252407
  reject(err);
252408
252408
  else
252409
- resolve5();
252409
+ resolve6();
252410
252410
  });
252411
252411
  });
252412
252412
  }
@@ -252414,12 +252414,12 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
252414
252414
  fs__namespace.renameSync(srcPath, destPath);
252415
252415
  }
252416
252416
  copy(srcPath, destPath) {
252417
- return new Promise((resolve5, reject) => {
252417
+ return new Promise((resolve6, reject) => {
252418
252418
  fs__namespace.copyFile(srcPath, destPath, (err) => {
252419
252419
  if (err)
252420
252420
  reject(err);
252421
252421
  else
252422
- resolve5();
252422
+ resolve6();
252423
252423
  });
252424
252424
  });
252425
252425
  }
@@ -252427,15 +252427,15 @@ type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "js
252427
252427
  fs__namespace.copyFileSync(srcPath, destPath);
252428
252428
  }
252429
252429
  stat(path2) {
252430
- return new Promise((resolve5, reject) => {
252430
+ return new Promise((resolve6, reject) => {
252431
252431
  fs__namespace.stat(path2, (err, stat) => {
252432
252432
  if (err) {
252433
252433
  if (err.code === "ENOENT" || err.code === "ENOTDIR")
252434
- resolve5(void 0);
252434
+ resolve6(void 0);
252435
252435
  else
252436
252436
  reject(err);
252437
252437
  } else {
252438
- resolve5(stat);
252438
+ resolve6(stat);
252439
252439
  }
252440
252440
  });
252441
252441
  });
@@ -276700,9 +276700,9 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
276700
276700
  };
276701
276701
 
276702
276702
  // src/cli/index.ts
276703
- var import_picocolors16 = __toESM(require_picocolors(), 1);
276704
- var import_fs11 = require("fs");
276705
- var import_path6 = require("path");
276703
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
276704
+ var import_fs12 = require("fs");
276705
+ var import_path7 = require("path");
276706
276706
  var import_child_process2 = require("child_process");
276707
276707
 
276708
276708
  // src/cli/migrate.ts
@@ -276775,6 +276775,7 @@ var ERROR_CODES = {
276775
276775
  issuer_register_forbidden: wire("permission", 403, false, "Registering a trusted issuer requires a secret (sk_) API key."),
276776
276776
  capability_invalid: wire("capability", 403, false, "The capability is unknown, revoked, or expired."),
276777
276777
  test_database_not_registered: wire("permission", 403, false, "Test mode requires a registered dev database for this org \u2014 run `npx ablo init`, or construct the client with `databaseUrl` using your test key."),
276778
+ tenant_routing_failed: wire("server", 500, true, "The org's registered database could not be resolved or dialed. Ablo never falls back to shared storage for a dedicated tenant \u2014 retry, and check the datasource status if it persists."),
276778
276779
  database_role_cannot_enforce_rls: wire("permission", 403, false, "The connected database role cannot enforce row-level security (superuser or BYPASSRLS)."),
276779
276780
  database_role_unreadable: wire("permission", 403, false, "The connected database role could not be introspected."),
276780
276781
  database_tables_unforced_rls: wire("permission", 403, false, "Synced tables in the connected database do not have FORCE ROW LEVEL SECURITY applied."),
@@ -276786,13 +276787,22 @@ var ERROR_CODES = {
276786
276787
  byo_tenant_tables_unforced_rls: wire("permission", 403, false, "Tenant tables do not have RLS forced under the direct Postgres connector role."),
276787
276788
  byo_host_not_allowed: wire("permission", 403, false, "The direct Postgres connector host resolves to a private, loopback, or link-local address and cannot be used."),
276788
276789
  // ── claim / intent conflict (409) ──────────────────────────────────
276789
- claim_conflict: wire("claim", 409, true, "The target entity is claimed by another participant."),
276790
- claim_lost: wire("claim", 409, true, "A previously held claim was lost before the write applied."),
276791
- entity_claimed: wire("claim", 409, true, "The target entity is currently claimed; write was blocked."),
276792
- intent_conflict: wire("claim", 409, true, "An intent on the target conflicts with an active intent (server-internal alias of claim_conflict)."),
276790
+ // Held-claim rejections are NOT queue-retryable (gRPC FAILED_PRECONDITION /
276791
+ // ABORTED semantics; Replicache/Zero SETTLE a rejected mutation reject the
276792
+ // caller, roll back the optimistic effect instead of resending it).
276793
+ // Blindly re-sending the same payload cannot succeed while the lease is
276794
+ // held, and a lease can outlive any sane retry budget. The correct recovery
276795
+ // lives at the CALLER: take a claim (`ablo.<model>.claim` queues fairly
276796
+ // behind the holder) or re-read and rebase. `retryable: true` here turned
276797
+ // every cross-client claim conflict into an infinite client resend loop
276798
+ // (~150ms storm — found by the claims journey, 2026-06-10).
276799
+ claim_conflict: wire("claim", 409, false, "The target entity is claimed by another participant."),
276800
+ claim_lost: wire("claim", 409, false, "A previously held claim was lost before the write applied."),
276801
+ entity_claimed: wire("claim", 409, false, "The target entity is currently claimed; write was blocked."),
276802
+ intent_conflict: wire("claim", 409, false, "An intent on the target conflicts with an active intent (server-internal alias of claim_conflict)."),
276793
276803
  malformed_claim: wire("claim", 400, false, "The claim payload was malformed."),
276794
- model_claimed: wire("claim", 409, true, "The model instance is claimed by another participant."),
276795
- model_claimed_timeout: wire("claim", 409, true, "Timed out waiting for a model claim to clear."),
276804
+ model_claimed: wire("claim", 409, false, "The model instance is claimed by another participant."),
276805
+ model_claimed_timeout: wire("claim", 409, false, "Timed out waiting for a model claim to clear."),
276796
276806
  model_claim_not_configured: client("claim", "Claiming was requested on a model that has no claim configuration."),
276797
276807
  // ── stale context / idempotency (409) ──────────────────────────────
276798
276808
  stale_context: wire("conflict", 409, true, "The write carried a readAt watermark that is now stale; re-read and retry."),
@@ -277087,8 +277097,8 @@ var ErrorBodyShapeSchema = import_zod2.z.object({
277087
277097
  }).passthrough();
277088
277098
 
277089
277099
  // src/cli/migrate.ts
277090
- var import_picocolors3 = __toESM(require_picocolors(), 1);
277091
- var import_fs4 = require("fs");
277100
+ var import_picocolors4 = __toESM(require_picocolors(), 1);
277101
+ var import_fs5 = require("fs");
277092
277102
 
277093
277103
  // node_modules/postgres/src/index.js
277094
277104
  init_cjs_shims();
@@ -277106,9 +277116,9 @@ var originError = /* @__PURE__ */ Symbol("OriginError");
277106
277116
  var CLOSE = {};
277107
277117
  var Query = class extends Promise {
277108
277118
  constructor(strings, args, handler, canceller, options = {}) {
277109
- let resolve5, reject;
277119
+ let resolve6, reject;
277110
277120
  super((a, b4) => {
277111
- resolve5 = a;
277121
+ resolve6 = a;
277112
277122
  reject = b4;
277113
277123
  });
277114
277124
  this.tagged = Array.isArray(strings.raw);
@@ -277119,7 +277129,7 @@ var Query = class extends Promise {
277119
277129
  this.options = options;
277120
277130
  this.state = null;
277121
277131
  this.statement = null;
277122
- this.resolve = (x2) => (this.active = false, resolve5(x2));
277132
+ this.resolve = (x2) => (this.active = false, resolve6(x2));
277123
277133
  this.reject = (x2) => (this.active = false, reject(x2));
277124
277134
  this.active = false;
277125
277135
  this.cancelled = null;
@@ -277167,12 +277177,12 @@ var Query = class extends Promise {
277167
277177
  if (this.executed && !this.active)
277168
277178
  return { done: true };
277169
277179
  prev && prev();
277170
- const promise = new Promise((resolve5, reject) => {
277180
+ const promise = new Promise((resolve6, reject) => {
277171
277181
  this.cursorFn = (value) => {
277172
- resolve5({ value, done: false });
277182
+ resolve6({ value, done: false });
277173
277183
  return new Promise((r2) => prev = r2);
277174
277184
  };
277175
- this.resolve = () => (this.active = false, resolve5({ done: true }));
277185
+ this.resolve = () => (this.active = false, resolve6({ done: true }));
277176
277186
  this.reject = (x2) => (this.active = false, reject(x2));
277177
277187
  });
277178
277188
  this.execute();
@@ -277804,12 +277814,12 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
277804
277814
  x2.on("drain", drain);
277805
277815
  return x2;
277806
277816
  }
277807
- async function cancel({ pid, secret }, resolve5, reject) {
277817
+ async function cancel({ pid, secret }, resolve6, reject) {
277808
277818
  try {
277809
277819
  cancelMessage = bytes_default().i32(16).i32(80877102).i32(pid).i32(secret).end(16);
277810
277820
  await connect();
277811
277821
  socket.once("error", reject);
277812
- socket.once("close", resolve5);
277822
+ socket.once("close", resolve6);
277813
277823
  } catch (error2) {
277814
277824
  reject(error2);
277815
277825
  }
@@ -278758,7 +278768,7 @@ function parseEvent(x2) {
278758
278768
  init_cjs_shims();
278759
278769
  var import_stream2 = __toESM(require("stream"), 1);
278760
278770
  function largeObject(sql, oid, mode2 = 131072 | 262144) {
278761
- return new Promise(async (resolve5, reject) => {
278771
+ return new Promise(async (resolve6, reject) => {
278762
278772
  await sql.begin(async (sql2) => {
278763
278773
  let finish;
278764
278774
  !oid && ([{ oid }] = await sql2`select lo_creat(-1) as oid`);
@@ -278784,7 +278794,7 @@ function largeObject(sql, oid, mode2 = 131072 | 262144) {
278784
278794
  ) seek
278785
278795
  `
278786
278796
  };
278787
- resolve5(lo);
278797
+ resolve6(lo);
278788
278798
  return new Promise(async (r2) => finish = r2);
278789
278799
  async function readable({
278790
278800
  highWaterMark = 2048 * 8,
@@ -278958,8 +278968,8 @@ function Postgres(a, b4) {
278958
278968
  }
278959
278969
  async function reserve() {
278960
278970
  const queue = queue_default();
278961
- const c = open.length ? open.shift() : await new Promise((resolve5, reject) => {
278962
- const query = { reserve: resolve5, reject };
278971
+ const c = open.length ? open.shift() : await new Promise((resolve6, reject) => {
278972
+ const query = { reserve: resolve6, reject };
278963
278973
  queries.push(query);
278964
278974
  closed.length && connect(closed.shift(), query);
278965
278975
  });
@@ -278996,9 +279006,9 @@ function Postgres(a, b4) {
278996
279006
  let uncaughtError, result;
278997
279007
  name && await sql2`savepoint ${sql2(name)}`;
278998
279008
  try {
278999
- result = await new Promise((resolve5, reject) => {
279009
+ result = await new Promise((resolve6, reject) => {
279000
279010
  const x2 = fn2(sql2);
279001
- Promise.resolve(Array.isArray(x2) ? Promise.all(x2) : x2).then(resolve5, reject);
279011
+ Promise.resolve(Array.isArray(x2) ? Promise.all(x2) : x2).then(resolve6, reject);
279002
279012
  });
279003
279013
  if (uncaughtError)
279004
279014
  throw uncaughtError;
@@ -279055,8 +279065,8 @@ function Postgres(a, b4) {
279055
279065
  return c.execute(query) ? move(c, busy) : move(c, full);
279056
279066
  }
279057
279067
  function cancel(query) {
279058
- return new Promise((resolve5, reject) => {
279059
- query.state ? query.active ? connection_default(options).cancel(query.state, resolve5, reject) : query.cancelled = { resolve: resolve5, reject } : (queries.remove(query), query.cancelled = true, query.reject(Errors.generic("57014", "canceling statement due to user request")), resolve5());
279068
+ return new Promise((resolve6, reject) => {
279069
+ query.state ? query.active ? connection_default(options).cancel(query.state, resolve6, reject) : query.cancelled = { resolve: resolve6, reject } : (queries.remove(query), query.cancelled = true, query.reject(Errors.generic("57014", "canceling statement due to user request")), resolve6());
279060
279070
  });
279061
279071
  }
279062
279072
  async function end({ timeout = null } = {}) {
@@ -279075,11 +279085,11 @@ function Postgres(a, b4) {
279075
279085
  async function close() {
279076
279086
  await Promise.all(connections.map((c) => c.end()));
279077
279087
  }
279078
- async function destroy(resolve5) {
279088
+ async function destroy(resolve6) {
279079
279089
  await Promise.all(connections.map((c) => c.terminate()));
279080
279090
  while (queries.length)
279081
279091
  queries.shift().reject(Errors.connection("CONNECTION_DESTROYED", options));
279082
- resolve5();
279092
+ resolve6();
279083
279093
  }
279084
279094
  function connect(c, query) {
279085
279095
  move(c, connecting);
@@ -279224,32 +279234,198 @@ function osUsername() {
279224
279234
  }
279225
279235
  }
279226
279236
 
279237
+ // src/cli/dbRole.ts
279238
+ init_cjs_shims();
279239
+ var import_crypto2 = require("crypto");
279240
+ var import_picocolors2 = __toESM(require_picocolors(), 1);
279241
+ var import_fs2 = require("fs");
279242
+ var import_path = require("path");
279243
+ var DEFAULT_SCOPED_ROLE = "ablo_app";
279244
+ async function detectRoleSafety(sql) {
279245
+ const rows = await sql`SELECT rolname, rolsuper, rolbypassrls FROM pg_roles WHERE rolname = current_user`;
279246
+ const row = rows[0];
279247
+ if (!row) return { role: "unknown", superuser: false, bypassRls: false, unsafe: false };
279248
+ return {
279249
+ role: row.rolname,
279250
+ superuser: row.rolsuper,
279251
+ bypassRls: row.rolbypassrls,
279252
+ unsafe: row.rolsuper || row.rolbypassrls
279253
+ };
279254
+ }
279255
+ function generateRolePassword() {
279256
+ return (0, import_crypto2.randomBytes)(24).toString("base64url");
279257
+ }
279258
+ function scramSha256Verifier(password, iterations = 4096) {
279259
+ const salt = (0, import_crypto2.randomBytes)(16);
279260
+ const saltedPassword = (0, import_crypto2.pbkdf2Sync)(password, salt, iterations, 32, "sha256");
279261
+ const clientKey = (0, import_crypto2.createHmac)("sha256", saltedPassword).update("Client Key").digest();
279262
+ const storedKey = (0, import_crypto2.createHash)("sha256").update(clientKey).digest();
279263
+ const serverKey = (0, import_crypto2.createHmac)("sha256", saltedPassword).update("Server Key").digest();
279264
+ return `SCRAM-SHA-256$${iterations}:${salt.toString("base64")}$${storedKey.toString("base64")}:${serverKey.toString("base64")}`;
279265
+ }
279266
+ function scopedRoleStatements(input) {
279267
+ const role = input.role ?? DEFAULT_SCOPED_ROLE;
279268
+ const q2 = (id) => `"${id.replace(/"/g, '""')}"`;
279269
+ const pw = (input.passwordMode ?? "scram-verifier") === "scram-verifier" ? scramSha256Verifier(input.password) : input.password.replace(/'/g, "''");
279270
+ return [
279271
+ `DO $$ BEGIN
279272
+ CREATE ROLE ${q2(role)} LOGIN PASSWORD '${pw}'
279273
+ NOSUPERUSER NOBYPASSRLS NOCREATEDB NOCREATEROLE;
279274
+ EXCEPTION WHEN duplicate_object THEN
279275
+ -- Rerun: rotate ONLY the password. Re-asserting attributes here trips
279276
+ -- managed-Postgres permission walls (Neon: "permission denied to alter
279277
+ -- role" for attribute changes by non-superusers); the attributes were set
279278
+ -- at creation, and the server-side probe still audits the live role.
279279
+ ALTER ROLE ${q2(role)} WITH LOGIN PASSWORD '${pw}';
279280
+ END $$;`,
279281
+ `GRANT CREATE, CONNECT ON DATABASE ${q2(input.database)} TO ${q2(role)};`,
279282
+ `GRANT CREATE, USAGE ON SCHEMA public TO ${q2(role)};`
279283
+ ];
279284
+ }
279285
+ function rewriteDatabaseUrl(ownerUrl, role, password) {
279286
+ const url = new URL(ownerUrl);
279287
+ url.username = role;
279288
+ url.password = password;
279289
+ return url.toString();
279290
+ }
279291
+ async function createScopedRole(ownerUrl, options) {
279292
+ const role = options?.role ?? DEFAULT_SCOPED_ROLE;
279293
+ const password = generateRolePassword();
279294
+ const database = new URL(ownerUrl).pathname.replace(/^\//, "") || "postgres";
279295
+ const sql = src_default(ownerUrl, { max: 1, prepare: false, onnotice: () => {
279296
+ } });
279297
+ try {
279298
+ try {
279299
+ for (const statement of scopedRoleStatements({ database, role, password })) {
279300
+ await sql.unsafe(statement);
279301
+ }
279302
+ } catch (err) {
279303
+ const message = err instanceof Error ? err.message : String(err);
279304
+ if (!/plaintext password/i.test(message)) throw err;
279305
+ for (const statement of scopedRoleStatements({ database, role, password, passwordMode: "plaintext" })) {
279306
+ await sql.unsafe(statement);
279307
+ }
279308
+ }
279309
+ } finally {
279310
+ await sql.end({ timeout: 5 });
279311
+ }
279312
+ return { role, databaseUrl: rewriteDatabaseUrl(ownerUrl, role, password) };
279313
+ }
279314
+ async function ensureScopedRoleInteractive(dbUrl) {
279315
+ let safety;
279316
+ try {
279317
+ const sql = src_default(dbUrl, { max: 1, prepare: false, onnotice: () => {
279318
+ } });
279319
+ try {
279320
+ safety = await detectRoleSafety(sql);
279321
+ } finally {
279322
+ await sql.end({ timeout: 5 });
279323
+ }
279324
+ } catch {
279325
+ return dbUrl;
279326
+ }
279327
+ if (!safety.unsafe) return dbUrl;
279328
+ const why = safety.superuser ? "a superuser" : "BYPASSRLS";
279329
+ console.log(
279330
+ `
279331
+ ${import_picocolors2.default.yellow("!")} DATABASE_URL connects as ${import_picocolors2.default.bold(safety.role)} \u2014 ${why}, so row-level security can't be enforced.
279332
+ Ablo's server will refuse this connection (${import_picocolors2.default.bold("database_role_cannot_enforce_rls")}).`
279333
+ );
279334
+ if (!process.stdout.isTTY) {
279335
+ console.log(
279336
+ import_picocolors2.default.dim(
279337
+ ` Create a scoped role and update DATABASE_URL \u2014 run \`npx ablo migrate\` interactively
279338
+ to do it automatically, or see https://docs.abloatai.com/quickstart#scoped-role`
279339
+ )
279340
+ );
279341
+ return dbUrl;
279342
+ }
279343
+ const proceed = await ye({
279344
+ message: `Create a scoped role ${DEFAULT_SCOPED_ROLE} (NOSUPERUSER, NOBYPASSRLS) and update DATABASE_URL?`,
279345
+ initialValue: true
279346
+ });
279347
+ if (pD(proceed) || !proceed) {
279348
+ console.log(import_picocolors2.default.dim(" Skipped \u2014 see https://docs.abloatai.com/quickstart#scoped-role for the manual recipe."));
279349
+ return dbUrl;
279350
+ }
279351
+ const { role, databaseUrl } = await createScopedRole(dbUrl);
279352
+ const where = persistDatabaseUrl(databaseUrl);
279353
+ console.log(
279354
+ ` ${import_picocolors2.default.green("\u2713")} Created role ${import_picocolors2.default.bold(role)} and updated ${import_picocolors2.default.bold("DATABASE_URL")} in ${import_picocolors2.default.bold(where)}.
279355
+ ` + import_picocolors2.default.dim(` The owner credential never left this machine; the new password was written, not printed.`)
279356
+ );
279357
+ return databaseUrl;
279358
+ }
279359
+ function persistDatabaseUrl(databaseUrl, cwd = process.cwd()) {
279360
+ const line = `DATABASE_URL=${databaseUrl}`;
279361
+ for (const name of [".env.local", ".env"]) {
279362
+ const path = (0, import_path.resolve)(cwd, name);
279363
+ if (!(0, import_fs2.existsSync)(path)) continue;
279364
+ const content = (0, import_fs2.readFileSync)(path, "utf8");
279365
+ if (/^DATABASE_URL=/m.test(content)) {
279366
+ (0, import_fs2.writeFileSync)(path, content.replace(/^DATABASE_URL=.*$/m, line));
279367
+ return name;
279368
+ }
279369
+ }
279370
+ const envLocal = (0, import_path.resolve)(cwd, ".env.local");
279371
+ if ((0, import_fs2.existsSync)(envLocal)) {
279372
+ const content = (0, import_fs2.readFileSync)(envLocal, "utf8");
279373
+ (0, import_fs2.appendFileSync)(envLocal, `${content.endsWith("\n") || content.length === 0 ? "" : "\n"}${line}
279374
+ `);
279375
+ } else {
279376
+ (0, import_fs2.writeFileSync)(envLocal, `${line}
279377
+ `, { mode: 384 });
279378
+ }
279379
+ const gitignorePath = (0, import_path.resolve)(cwd, ".gitignore");
279380
+ const gitignore = (0, import_fs2.existsSync)(gitignorePath) ? (0, import_fs2.readFileSync)(gitignorePath, "utf8") : "";
279381
+ if (!/^(\.env\.local|\.env\*|\.env\.\*|\.env.*)$/m.test(gitignore)) {
279382
+ (0, import_fs2.writeFileSync)(
279383
+ gitignorePath,
279384
+ `${gitignore.endsWith("\n") || gitignore.length === 0 ? gitignore : `${gitignore}
279385
+ `}.env.local
279386
+ `
279387
+ );
279388
+ }
279389
+ return ".env.local";
279390
+ }
279391
+ function readProjectDatabaseUrl(cwd = process.cwd()) {
279392
+ const fromEnv = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
279393
+ if (fromEnv) return fromEnv;
279394
+ for (const name of [".env.local", ".env"]) {
279395
+ const path = (0, import_path.resolve)(cwd, name);
279396
+ if (!(0, import_fs2.existsSync)(path)) continue;
279397
+ const match = (0, import_fs2.readFileSync)(path, "utf8").match(/^DATABASE_URL=(.+)$/m);
279398
+ if (match?.[1]) return match[1].trim().replace(/^["']|["']$/g, "");
279399
+ }
279400
+ return null;
279401
+ }
279402
+
279227
279403
  // src/cli/migrate.ts
279228
279404
  var import_schema2 = require("@abloatai/ablo/schema");
279229
279405
  var import_source = require("@abloatai/ablo/source");
279230
279406
 
279231
279407
  // src/cli/push.ts
279232
279408
  init_cjs_shims();
279233
- var import_picocolors2 = __toESM(require_picocolors(), 1);
279234
- var import_fs3 = require("fs");
279235
- var import_path2 = require("path");
279409
+ var import_picocolors3 = __toESM(require_picocolors(), 1);
279410
+ var import_fs4 = require("fs");
279411
+ var import_path3 = require("path");
279236
279412
  var import_schema = require("@abloatai/ablo/schema");
279237
279413
 
279238
279414
  // src/cli/config.ts
279239
279415
  init_cjs_shims();
279240
279416
  var import_os2 = require("os");
279241
- var import_path = require("path");
279242
- var import_fs2 = require("fs");
279417
+ var import_path2 = require("path");
279418
+ var import_fs3 = require("fs");
279243
279419
  function configDir() {
279244
279420
  if (process.env.ABLO_CONFIG_DIR) return process.env.ABLO_CONFIG_DIR;
279245
279421
  const xdg = process.env.XDG_CONFIG_HOME;
279246
- return xdg ? (0, import_path.join)(xdg, "ablo") : (0, import_path.join)((0, import_os2.homedir)(), ".config", "ablo");
279422
+ return xdg ? (0, import_path2.join)(xdg, "ablo") : (0, import_path2.join)((0, import_os2.homedir)(), ".config", "ablo");
279247
279423
  }
279248
279424
  function configPath() {
279249
- return (0, import_path.join)(configDir(), "config.json");
279425
+ return (0, import_path2.join)(configDir(), "config.json");
279250
279426
  }
279251
279427
  function credentialsPath() {
279252
- return (0, import_path.join)(configDir(), "credentials.json");
279428
+ return (0, import_path2.join)(configDir(), "credentials.json");
279253
279429
  }
279254
279430
  function asKeyEntry(value) {
279255
279431
  if (value && typeof value === "object" && typeof value.apiKey === "string") {
@@ -279258,9 +279434,9 @@ function asKeyEntry(value) {
279258
279434
  return void 0;
279259
279435
  }
279260
279436
  function readJson(path) {
279261
- if (!(0, import_fs2.existsSync)(path)) return null;
279437
+ if (!(0, import_fs3.existsSync)(path)) return null;
279262
279438
  try {
279263
- const parsed = JSON.parse((0, import_fs2.readFileSync)(path, "utf-8"));
279439
+ const parsed = JSON.parse((0, import_fs3.readFileSync)(path, "utf-8"));
279264
279440
  return parsed && typeof parsed === "object" ? parsed : null;
279265
279441
  } catch {
279266
279442
  return null;
@@ -279297,14 +279473,14 @@ function readConfig() {
279297
279473
  }
279298
279474
  function writeConfig(cfg) {
279299
279475
  const dir = configDir();
279300
- (0, import_fs2.mkdirSync)(dir, { recursive: true, mode: 448 });
279301
- (0, import_fs2.writeFileSync)(configPath(), `${JSON.stringify({ mode: cfg.mode }, null, 2)}
279476
+ (0, import_fs3.mkdirSync)(dir, { recursive: true, mode: 448 });
279477
+ (0, import_fs3.writeFileSync)(configPath(), `${JSON.stringify({ mode: cfg.mode }, null, 2)}
279302
279478
  `, { mode: 384 });
279303
279479
  const credentials = {
279304
279480
  ...cfg.sandbox ? { sandbox: cfg.sandbox } : {},
279305
279481
  ...cfg.production ? { production: cfg.production } : {}
279306
279482
  };
279307
- (0, import_fs2.writeFileSync)(credentialsPath(), `${JSON.stringify(credentials, null, 2)}
279483
+ (0, import_fs3.writeFileSync)(credentialsPath(), `${JSON.stringify(credentials, null, 2)}
279308
279484
  `, { mode: 384 });
279309
279485
  return credentialsPath();
279310
279486
  }
@@ -279325,8 +279501,8 @@ function normalizeMode(value) {
279325
279501
  function clearCredential() {
279326
279502
  let removed = false;
279327
279503
  for (const path of [configPath(), credentialsPath()]) {
279328
- if ((0, import_fs2.existsSync)(path)) {
279329
- (0, import_fs2.rmSync)(path);
279504
+ if ((0, import_fs3.existsSync)(path)) {
279505
+ (0, import_fs3.rmSync)(path);
279330
279506
  removed = true;
279331
279507
  }
279332
279508
  }
@@ -279355,7 +279531,7 @@ var DEFAULT_URL = "https://api.abloatai.com";
279355
279531
  function fmtSignal(s) {
279356
279532
  const sig = s;
279357
279533
  const where = sig.field ? `${sig.model}.${sig.field}` : sig.model;
279358
- return ` \u2022 ${import_picocolors2.default.bold(where ?? "?")} \u2014 ${sig.detail ?? ""}`;
279534
+ return ` \u2022 ${import_picocolors3.default.bold(where ?? "?")} \u2014 ${sig.detail ?? ""}`;
279359
279535
  }
279360
279536
  async function pushSchema(schema, args) {
279361
279537
  const schemaJson = JSON.parse((0, import_schema.serializeSchema)(schema));
@@ -279433,10 +279609,10 @@ function parsePushArgs(argv) {
279433
279609
  return { schemaPath, exportName, url, apiKey: process.env.ABLO_API_KEY, force, renames, backfills };
279434
279610
  }
279435
279611
  async function loadSchema(schemaPath, exportName) {
279436
- const abs = (0, import_path2.resolve)(process.cwd(), schemaPath);
279437
- if (!(0, import_fs3.existsSync)(abs)) {
279612
+ const abs = (0, import_path3.resolve)(process.cwd(), schemaPath);
279613
+ if (!(0, import_fs4.existsSync)(abs)) {
279438
279614
  throw new AbloValidationError(
279439
- `schema not found at ${import_picocolors2.default.bold(schemaPath)}. Run ${import_picocolors2.default.bold("npx ablo init")} or pass ${import_picocolors2.default.bold("--schema <path>")}.`,
279615
+ `schema not found at ${import_picocolors3.default.bold(schemaPath)}. Run ${import_picocolors3.default.bold("npx ablo init")} or pass ${import_picocolors3.default.bold("--schema <path>")}.`,
279440
279616
  { code: "cli_invalid_arguments" }
279441
279617
  );
279442
279618
  }
@@ -279447,7 +279623,7 @@ async function loadSchema(schemaPath, exportName) {
279447
279623
  const schema = mod[exportName] ?? nested?.[exportName];
279448
279624
  if (!schema || typeof schema !== "object" || !("models" in schema)) {
279449
279625
  throw new AbloValidationError(
279450
- `${import_picocolors2.default.bold(schemaPath)} has no \`${exportName}\` export that looks like a Schema. Did you \`export const ${exportName} = defineSchema({ ... })\`?`,
279626
+ `${import_picocolors3.default.bold(schemaPath)} has no \`${exportName}\` export that looks like a Schema. Did you \`export const ${exportName} = defineSchema({ ... })\`?`,
279451
279627
  { code: "cli_invalid_arguments" }
279452
279628
  );
279453
279629
  }
@@ -279458,30 +279634,30 @@ async function push(argv) {
279458
279634
  try {
279459
279635
  args = parsePushArgs(argv);
279460
279636
  } catch (err) {
279461
- console.error(import_picocolors2.default.red(` ${err instanceof Error ? err.message : String(err)}`));
279637
+ console.error(import_picocolors3.default.red(` ${err instanceof Error ? err.message : String(err)}`));
279462
279638
  process.exit(1);
279463
279639
  }
279464
279640
  if (!args.apiKey) args.apiKey = resolveApiKey();
279465
279641
  if (!args.apiKey) {
279466
279642
  console.error(
279467
- import_picocolors2.default.red(` No API key.`) + import_picocolors2.default.dim(` Run ${import_picocolors2.default.bold("ablo login")} or set ${import_picocolors2.default.bold("ABLO_API_KEY")} (a secret sk_ key with schema:push).`)
279643
+ import_picocolors3.default.red(` No API key.`) + import_picocolors3.default.dim(` Run ${import_picocolors3.default.bold("ablo login")} or set ${import_picocolors3.default.bold("ABLO_API_KEY")} (a secret sk_ key with schema:push).`)
279468
279644
  );
279469
279645
  process.exit(1);
279470
279646
  }
279471
279647
  const schema = await loadSchema(args.schemaPath, args.exportName);
279472
279648
  const hash = (0, import_schema.schemaHash)(schema);
279473
279649
  console.log(
279474
- ` Pushing ${import_picocolors2.default.bold(args.schemaPath)} ${import_picocolors2.default.dim(`(${Object.keys(schema.models).length} models, hash ${hash})`)} \u2192 ${import_picocolors2.default.dim(args.url)}`
279650
+ ` Pushing ${import_picocolors3.default.bold(args.schemaPath)} ${import_picocolors3.default.dim(`(${Object.keys(schema.models).length} models, hash ${hash})`)} \u2192 ${import_picocolors3.default.dim(args.url)}`
279475
279651
  );
279476
279652
  const { ok: resOk, status: status2, body, bodyText } = await pushSchema(schema, args);
279477
279653
  if (resOk) {
279478
279654
  if (body.unchanged) {
279479
- console.log(` ${import_picocolors2.default.dim("\u25CB")} No changes \u2014 schema already active (v${body.version}).`);
279655
+ console.log(` ${import_picocolors3.default.dim("\u25CB")} No changes \u2014 schema already active (v${body.version}).`);
279480
279656
  } else {
279481
- console.log(` ${import_picocolors2.default.green("\u2713")} Activated ${import_picocolors2.default.bold(`v${body.version}`)} ${import_picocolors2.default.dim(`(hash ${body.hash})`)}`);
279657
+ console.log(` ${import_picocolors3.default.green("\u2713")} Activated ${import_picocolors3.default.bold(`v${body.version}`)} ${import_picocolors3.default.dim(`(hash ${body.hash})`)}`);
279482
279658
  if (Array.isArray(body.warnings) && body.warnings.length > 0) {
279483
- console.log(import_picocolors2.default.yellow(` Applied ${body.warnings.length} destructive change(s):`));
279484
- for (const w2 of body.warnings) console.log(import_picocolors2.default.yellow(fmtSignal(w2)));
279659
+ console.log(import_picocolors3.default.yellow(` Applied ${body.warnings.length} destructive change(s):`));
279660
+ for (const w2 of body.warnings) console.log(import_picocolors3.default.yellow(fmtSignal(w2)));
279485
279661
  }
279486
279662
  }
279487
279663
  return;
@@ -279489,20 +279665,20 @@ async function push(argv) {
279489
279665
  if (status2 === 409) {
279490
279666
  const unexecutable = Array.isArray(body.unexecutable) ? body.unexecutable : [];
279491
279667
  const warnings = Array.isArray(body.warnings) ? body.warnings : [];
279492
- console.error(import_picocolors2.default.red(" Incompatible change \u2014 this push is not safe to apply as-is."));
279668
+ console.error(import_picocolors3.default.red(" Incompatible change \u2014 this push is not safe to apply as-is."));
279493
279669
  if (unexecutable.length > 0) {
279494
- console.error(import_picocolors2.default.red(` Unexecutable (would fail on existing rows):`));
279495
- for (const u2 of unexecutable) console.error(import_picocolors2.default.red(fmtSignal(u2)));
279670
+ console.error(import_picocolors3.default.red(` Unexecutable (would fail on existing rows):`));
279671
+ for (const u2 of unexecutable) console.error(import_picocolors3.default.red(fmtSignal(u2)));
279496
279672
  }
279497
279673
  if (warnings.length > 0) {
279498
- console.error(import_picocolors2.default.yellow(` Destructive (data loss):`));
279499
- for (const w2 of warnings) console.error(import_picocolors2.default.yellow(fmtSignal(w2)));
279674
+ console.error(import_picocolors3.default.yellow(` Destructive (data loss):`));
279675
+ for (const w2 of warnings) console.error(import_picocolors3.default.yellow(fmtSignal(w2)));
279500
279676
  }
279501
- console.error(import_picocolors2.default.dim(` Re-push with ${import_picocolors2.default.bold("--force")} to override, or use ${import_picocolors2.default.bold("--rename old:new")} if you renamed a model.`));
279677
+ console.error(import_picocolors3.default.dim(` Re-push with ${import_picocolors3.default.bold("--force")} to override, or use ${import_picocolors3.default.bold("--rename old:new")} if you renamed a model.`));
279502
279678
  } else if (status2 === 403) {
279503
- console.error(import_picocolors2.default.red(` Forbidden: ${body.reason ?? "key lacks schema:push scope"}.`));
279679
+ console.error(import_picocolors3.default.red(` Forbidden: ${body.message ?? body.reason ?? "key lacks schema:push scope"}`));
279504
279680
  } else {
279505
- console.error(import_picocolors2.default.red(` Push failed (${status2}): ${body.reason ?? bodyText}`));
279681
+ console.error(import_picocolors3.default.red(` Push failed (${status2}): ${body.message ?? body.reason ?? bodyText}`));
279506
279682
  }
279507
279683
  process.exit(1);
279508
279684
  }
@@ -279550,8 +279726,8 @@ function planFor(schema, targetSchema = "public") {
279550
279726
  }
279551
279727
  var log = {
279552
279728
  info: (msg, fields) => console.log(`[migrate] ${msg}`, fields),
279553
- warn: (msg, fields) => console.warn(import_picocolors3.default.yellow(`[migrate] ${msg}`), fields),
279554
- error: (msg, fields) => console.error(import_picocolors3.default.red(`[migrate] ${msg}`), fields)
279729
+ warn: (msg, fields) => console.warn(import_picocolors4.default.yellow(`[migrate] ${msg}`), fields),
279730
+ error: (msg, fields) => console.error(import_picocolors4.default.red(`[migrate] ${msg}`), fields)
279555
279731
  };
279556
279732
  var PG_LOCK_NOT_AVAILABLE = "55P03";
279557
279733
  var LOCK_TIMEOUT = process.env.ABLO_SCHEMA_LOCK_TIMEOUT ?? process.env.ABLO_DDL_LOCK_TIMEOUT ?? "5s";
@@ -279595,7 +279771,7 @@ async function applyStatements(dbUrl, targetSchema, statements, concurrent = [])
279595
279771
  if (pg.code === PG_LOCK_NOT_AVAILABLE && attempt < MAX_LOCK_ATTEMPTS) {
279596
279772
  const backoffMs = Math.min(6e4, 10 * 2 ** attempt) + Math.floor(Math.random() * 50);
279597
279773
  log.warn("schema change blocked by a lock; backing off and retrying", { targetSchema, attempt, backoffMs });
279598
- await new Promise((resolve5) => setTimeout(resolve5, backoffMs));
279774
+ await new Promise((resolve6) => setTimeout(resolve6, backoffMs));
279599
279775
  continue;
279600
279776
  }
279601
279777
  throw err;
@@ -279626,7 +279802,7 @@ async function migrate(argv) {
279626
279802
  try {
279627
279803
  args = parseMigrateArgs(argv);
279628
279804
  } catch (err) {
279629
- console.error(import_picocolors3.default.red(` ${err instanceof Error ? err.message : String(err)}`));
279805
+ console.error(import_picocolors4.default.red(` ${err instanceof Error ? err.message : String(err)}`));
279630
279806
  process.exit(1);
279631
279807
  }
279632
279808
  const schema = await loadSchema(args.schemaPath, args.exportName);
@@ -279637,11 +279813,11 @@ async function migrate(argv) {
279637
279813
  ].join("\n");
279638
279814
  const totalStatements = plan.statements.length + plan.concurrent.length;
279639
279815
  console.log(
279640
- ` ${import_picocolors3.default.dim("Schema")} ${import_picocolors3.default.bold(args.schemaPath)} \u2192 ${import_picocolors3.default.dim(`${Object.keys(schema.models).length} models, ${totalStatements} statements`)}`
279816
+ ` ${import_picocolors4.default.dim("Schema")} ${import_picocolors4.default.bold(args.schemaPath)} \u2192 ${import_picocolors4.default.dim(`${Object.keys(schema.models).length} models, ${totalStatements} statements`)}`
279641
279817
  );
279642
279818
  if (args.outputFile) {
279643
- (0, import_fs4.writeFileSync)(args.outputFile, sql + "\n");
279644
- console.log(` ${import_picocolors3.default.green("\u2713")} SQL written to ${import_picocolors3.default.bold(args.outputFile)}`);
279819
+ (0, import_fs5.writeFileSync)(args.outputFile, sql + "\n");
279820
+ console.log(` ${import_picocolors4.default.green("\u2713")} SQL written to ${import_picocolors4.default.bold(args.outputFile)}`);
279645
279821
  return;
279646
279822
  }
279647
279823
  if (args.dryRun) {
@@ -279650,12 +279826,13 @@ async function migrate(argv) {
279650
279826
  }
279651
279827
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
279652
279828
  if (!dbUrl) {
279653
- console.error(import_picocolors3.default.red(" Set DATABASE_URL (or ABLO_DATABASE_URL) to apply, or use --dry-run to preview."));
279829
+ console.error(import_picocolors4.default.red(" Set DATABASE_URL (or ABLO_DATABASE_URL) to apply, or use --dry-run to preview."));
279654
279830
  process.exit(1);
279655
279831
  }
279832
+ const effectiveUrl = await ensureScopedRoleInteractive(dbUrl);
279656
279833
  try {
279657
- await applyStatements(dbUrl, args.targetSchema, plan.statements, plan.concurrent);
279658
- console.log(` ${import_picocolors3.default.green("\u2713")} Migration complete`);
279834
+ await applyStatements(effectiveUrl, args.targetSchema, plan.statements, plan.concurrent);
279835
+ console.log(` ${import_picocolors4.default.green("\u2713")} Migration complete`);
279659
279836
  } catch {
279660
279837
  process.exit(1);
279661
279838
  }
@@ -279663,9 +279840,9 @@ async function migrate(argv) {
279663
279840
 
279664
279841
  // src/cli/generate.ts
279665
279842
  init_cjs_shims();
279666
- var import_fs5 = require("fs");
279667
- var import_path3 = require("path");
279668
- var import_picocolors4 = __toESM(require_picocolors(), 1);
279843
+ var import_fs6 = require("fs");
279844
+ var import_path4 = require("path");
279845
+ var import_picocolors5 = __toESM(require_picocolors(), 1);
279669
279846
  var import_schema3 = require("@abloatai/ablo/schema");
279670
279847
  var DEFAULT_SCHEMA_PATH3 = "ablo/schema.ts";
279671
279848
  var DEFAULT_EXPORT3 = "schema";
@@ -279697,7 +279874,7 @@ async function generate(argv) {
279697
279874
  try {
279698
279875
  args = parseGenerateArgs(argv);
279699
279876
  } catch (err) {
279700
- console.error(import_picocolors4.default.red(` ${err instanceof Error ? err.message : String(err)}`));
279877
+ console.error(import_picocolors5.default.red(` ${err instanceof Error ? err.message : String(err)}`));
279701
279878
  process.exit(1);
279702
279879
  }
279703
279880
  let source;
@@ -279706,20 +279883,20 @@ async function generate(argv) {
279706
279883
  const schemaJson = JSON.parse((0, import_schema3.serializeSchema)(schema));
279707
279884
  source = (0, import_schema3.generateTypes)(schemaJson);
279708
279885
  } catch (err) {
279709
- console.error(import_picocolors4.default.red(` ${err instanceof Error ? err.message : String(err)}`));
279886
+ console.error(import_picocolors5.default.red(` ${err instanceof Error ? err.message : String(err)}`));
279710
279887
  process.exit(1);
279711
279888
  }
279712
- const abs = (0, import_path3.resolve)(process.cwd(), args.out);
279713
- (0, import_fs5.mkdirSync)((0, import_path3.dirname)(abs), { recursive: true });
279714
- (0, import_fs5.writeFileSync)(abs, source);
279715
- console.log(` ${import_picocolors4.default.green("\u2713")} Generated types \u2192 ${import_picocolors4.default.bold(args.out)}`);
279889
+ const abs = (0, import_path4.resolve)(process.cwd(), args.out);
279890
+ (0, import_fs6.mkdirSync)((0, import_path4.dirname)(abs), { recursive: true });
279891
+ (0, import_fs6.writeFileSync)(abs, source);
279892
+ console.log(` ${import_picocolors5.default.green("\u2713")} Generated types \u2192 ${import_picocolors5.default.bold(args.out)}`);
279716
279893
  }
279717
279894
 
279718
279895
  // src/cli/dev.ts
279719
279896
  init_cjs_shims();
279720
- var import_picocolors5 = __toESM(require_picocolors(), 1);
279721
- var import_fs6 = require("fs");
279722
- var import_path4 = require("path");
279897
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
279898
+ var import_fs7 = require("fs");
279899
+ var import_path5 = require("path");
279723
279900
  var import_schema4 = require("@abloatai/ablo/schema");
279724
279901
 
279725
279902
  // src/cli/theme.ts
@@ -279740,7 +279917,7 @@ function parseDevArgs(argv) {
279740
279917
  let schemaPath = DEFAULT_SCHEMA_PATH;
279741
279918
  let exportName = DEFAULT_EXPORT;
279742
279919
  let url = process.env.ABLO_API_URL ?? DEFAULT_URL;
279743
- let watchEnabled = true;
279920
+ let watchEnabled = false;
279744
279921
  for (let i = 0; i < argv.length; i++) {
279745
279922
  const arg = argv[i];
279746
279923
  switch (arg) {
@@ -279753,6 +279930,9 @@ function parseDevArgs(argv) {
279753
279930
  case "--url":
279754
279931
  url = argv[++i] ?? url;
279755
279932
  break;
279933
+ case "--watch":
279934
+ watchEnabled = true;
279935
+ break;
279756
279936
  case "--no-watch":
279757
279937
  watchEnabled = false;
279758
279938
  break;
@@ -279767,58 +279947,58 @@ function classifyKey(apiKey) {
279767
279947
  if (!apiKey) {
279768
279948
  return {
279769
279949
  ok: false,
279770
- reason: `No API key. Run ${import_picocolors5.default.bold("ablo login")} (or set ${import_picocolors5.default.bold("ABLO_API_KEY")}) with a ${import_picocolors5.default.bold("sk_test_")} key from ${import_picocolors5.default.cyan("https://abloatai.com")}.`
279950
+ reason: `No API key. Run ${import_picocolors6.default.bold("ablo login")} (or set ${import_picocolors6.default.bold("ABLO_API_KEY")}) with a ${import_picocolors6.default.bold("sk_test_")} key from ${import_picocolors6.default.cyan("https://abloatai.com")}.`
279771
279951
  };
279772
279952
  }
279773
279953
  if (apiKey.startsWith("sk_test_")) return { ok: true };
279774
279954
  if (apiKey.startsWith("sk_live_")) {
279775
279955
  return {
279776
279956
  ok: false,
279777
- reason: `${import_picocolors5.default.bold("ablo dev")} refuses production keys. Use a ${import_picocolors5.default.bold("sk_test_")} key so the watch loop can't churn production data.`
279957
+ reason: `${import_picocolors6.default.bold("ablo dev")} refuses production keys. Use a ${import_picocolors6.default.bold("sk_test_")} key so the watch loop can't churn production data.`
279778
279958
  };
279779
279959
  }
279780
279960
  if (apiKey.startsWith("rk_")) {
279781
279961
  return {
279782
279962
  ok: false,
279783
- reason: `Restricted (${import_picocolors5.default.bold("rk_")}) keys can't push schema. Use a secret ${import_picocolors5.default.bold("sk_test_")} key.`
279963
+ reason: `Restricted (${import_picocolors6.default.bold("rk_")}) keys can't push schema. Use a secret ${import_picocolors6.default.bold("sk_test_")} key.`
279784
279964
  };
279785
279965
  }
279786
- return { ok: false, reason: `${import_picocolors5.default.bold("ABLO_API_KEY")} is not an Ablo key (expected ${import_picocolors5.default.bold("sk_test_\u2026")}).` };
279966
+ return { ok: false, reason: `${import_picocolors6.default.bold("ABLO_API_KEY")} is not an Ablo key (expected ${import_picocolors6.default.bold("sk_test_\u2026")}).` };
279787
279967
  }
279788
279968
  function wireEnvLocal(apiKey, cwd = process.cwd()) {
279789
- const envPath = (0, import_path4.resolve)(cwd, ".env.local");
279969
+ const envPath = (0, import_path5.resolve)(cwd, ".env.local");
279790
279970
  const line = `ABLO_API_KEY=${apiKey}`;
279791
279971
  let action;
279792
- if (!(0, import_fs6.existsSync)(envPath)) {
279793
- (0, import_fs6.writeFileSync)(envPath, `${line}
279972
+ if (!(0, import_fs7.existsSync)(envPath)) {
279973
+ (0, import_fs7.writeFileSync)(envPath, `${line}
279794
279974
  `, { mode: 384 });
279795
- action = `Created ${import_picocolors5.default.bold(".env.local")} with ${import_picocolors5.default.bold("ABLO_API_KEY")}`;
279975
+ action = `Created ${import_picocolors6.default.bold(".env.local")} with ${import_picocolors6.default.bold("ABLO_API_KEY")}`;
279796
279976
  } else {
279797
- const content = (0, import_fs6.readFileSync)(envPath, "utf8");
279977
+ const content = (0, import_fs7.readFileSync)(envPath, "utf8");
279798
279978
  const match = content.match(/^ABLO_API_KEY=(.*)$/m);
279799
279979
  if (!match) {
279800
- (0, import_fs6.appendFileSync)(envPath, `${content.endsWith("\n") || content.length === 0 ? "" : "\n"}${line}
279980
+ (0, import_fs7.appendFileSync)(envPath, `${content.endsWith("\n") || content.length === 0 ? "" : "\n"}${line}
279801
279981
  `);
279802
- action = `Added ${import_picocolors5.default.bold("ABLO_API_KEY")} to ${import_picocolors5.default.bold(".env.local")}`;
279982
+ action = `Added ${import_picocolors6.default.bold("ABLO_API_KEY")} to ${import_picocolors6.default.bold(".env.local")}`;
279803
279983
  } else if (match[1] === apiKey) {
279804
- action = `${import_picocolors5.default.bold(".env.local")} already has this key`;
279984
+ action = `${import_picocolors6.default.bold(".env.local")} already has this key`;
279805
279985
  } else {
279806
- (0, import_fs6.writeFileSync)(envPath, content.replace(/^ABLO_API_KEY=.*$/m, line));
279807
- action = `Updated ${import_picocolors5.default.bold("ABLO_API_KEY")} in ${import_picocolors5.default.bold(".env.local")} ${import_picocolors5.default.dim(`(was ${match[1].slice(0, 12)}\u2026)`)}`;
279986
+ (0, import_fs7.writeFileSync)(envPath, content.replace(/^ABLO_API_KEY=.*$/m, line));
279987
+ action = `Updated ${import_picocolors6.default.bold("ABLO_API_KEY")} in ${import_picocolors6.default.bold(".env.local")} ${import_picocolors6.default.dim(`(was ${match[1].slice(0, 12)}\u2026)`)}`;
279808
279988
  }
279809
279989
  }
279810
- const gitignorePath = (0, import_path4.resolve)(cwd, ".gitignore");
279811
- const gitignore = (0, import_fs6.existsSync)(gitignorePath) ? (0, import_fs6.readFileSync)(gitignorePath, "utf8") : "";
279990
+ const gitignorePath = (0, import_path5.resolve)(cwd, ".gitignore");
279991
+ const gitignore = (0, import_fs7.existsSync)(gitignorePath) ? (0, import_fs7.readFileSync)(gitignorePath, "utf8") : "";
279812
279992
  const ignored = /^(\.env\.local|\.env\*|\.env\.\*|\.env.*)$/m.test(gitignore);
279813
279993
  let gitignoreNote = "";
279814
279994
  if (!ignored) {
279815
- (0, import_fs6.writeFileSync)(
279995
+ (0, import_fs7.writeFileSync)(
279816
279996
  gitignorePath,
279817
279997
  `${gitignore.endsWith("\n") || gitignore.length === 0 ? gitignore : `${gitignore}
279818
279998
  `}.env.local
279819
279999
  `
279820
280000
  );
279821
- gitignoreNote = ` Added ${import_picocolors5.default.bold(".env.local")} to ${import_picocolors5.default.bold(".gitignore")} so the key can't be committed.`;
280001
+ gitignoreNote = ` Added ${import_picocolors6.default.bold(".env.local")} to ${import_picocolors6.default.bold(".gitignore")} so the key can't be committed.`;
279822
280002
  }
279823
280003
  return `${action}.${gitignoreNote}`;
279824
280004
  }
@@ -279833,7 +280013,7 @@ async function runPush(schema, args) {
279833
280013
  if (ok) {
279834
280014
  return {
279835
280015
  ok: true,
279836
- message: body.unchanged ? `schema unchanged ${import_picocolors5.default.dim(`(v${body.version})`)}` : `schema pushed (sandbox) ${import_picocolors5.default.dim(`(v${body.version}, hash ${body.hash})`)}`
280016
+ message: body.unchanged ? `schema unchanged ${import_picocolors6.default.dim(`(v${body.version})`)}` : `schema pushed (sandbox) ${import_picocolors6.default.dim(`(v${body.version}, hash ${body.hash})`)}`
279837
280017
  };
279838
280018
  }
279839
280019
  if (status2 === 409) {
@@ -279841,47 +280021,49 @@ async function runPush(schema, args) {
279841
280021
  const warnings = Array.isArray(body.warnings) ? body.warnings : [];
279842
280022
  const lines = [
279843
280023
  "Incompatible schema change \u2014 not safe to apply as-is.",
279844
- ...unexecutable.map((u2) => import_picocolors5.default.red(fmtSignal(u2))),
279845
- ...warnings.map((w2) => import_picocolors5.default.yellow(fmtSignal(w2))),
279846
- import_picocolors5.default.dim(`Run ${import_picocolors5.default.bold("ablo push --force")} (or ${import_picocolors5.default.bold("--rename old:new")}) to resolve.`)
280024
+ ...unexecutable.map((u2) => import_picocolors6.default.red(fmtSignal(u2))),
280025
+ ...warnings.map((w2) => import_picocolors6.default.yellow(fmtSignal(w2))),
280026
+ import_picocolors6.default.dim(`Run ${import_picocolors6.default.bold("ablo push --force")} (or ${import_picocolors6.default.bold("--rename old:new")}) to resolve.`)
279847
280027
  ];
279848
280028
  return { ok: false, message: lines.join("\n") };
279849
280029
  }
279850
280030
  if (status2 === 403) {
280031
+ const serverSays = body.message ?? body.reason;
280032
+ const hint = body.code === "database_role_cannot_enforce_rls" ? `Run ${import_picocolors6.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_picocolors6.default.bold("sandbox")} key with ${import_picocolors6.default.bold("schema:push")} \u2014 manage keys at ${import_picocolors6.default.cyan("https://abloatai.com")}.`;
279851
280033
  return {
279852
280034
  ok: false,
279853
- message: `This key can't author schema (${body.reason ?? "missing schema:push scope"}).
279854
- ` + import_picocolors5.default.dim(
279855
- `Use a ${import_picocolors5.default.bold("sandbox")} key, or one with ${import_picocolors5.default.bold("schema authoring")} enabled at ${import_picocolors5.default.cyan("https://abloatai.com")}.`
279856
- )
280035
+ message: `${serverSays ?? "This key can't author schema (missing schema:push scope)."}
280036
+ ` + import_picocolors6.default.dim(hint)
279857
280037
  };
279858
280038
  }
279859
- return { ok: false, message: `Push failed (${status2}): ${body.reason ?? bodyText}` };
280039
+ return { ok: false, message: `Push failed (${status2}): ${body.message ?? body.reason ?? bodyText}` };
279860
280040
  }
279861
280041
  async function dev(argv) {
279862
280042
  let args;
279863
280043
  try {
279864
280044
  args = parseDevArgs(argv);
279865
280045
  } catch (err) {
279866
- console.error(import_picocolors5.default.red(` ${err instanceof Error ? err.message : String(err)}`));
280046
+ console.error(import_picocolors6.default.red(` ${err instanceof Error ? err.message : String(err)}`));
279867
280047
  process.exit(1);
279868
280048
  }
279869
280049
  if (!args.apiKey) args.apiKey = resolveApiKey("sandbox");
279870
280050
  const key = classifyKey(args.apiKey);
279871
280051
  if (!key.ok) {
279872
- console.error(import_picocolors5.default.red(` ${key.reason}`));
280052
+ console.error(import_picocolors6.default.red(` ${key.reason}`));
279873
280053
  process.exit(1);
279874
280054
  }
279875
280055
  console.log(`
279876
- ${brand("ablo")} ${import_picocolors5.default.dim("sync engine \u2014 dev")} ${import_picocolors5.default.dim("(sandbox)")}
280056
+ ${brand("ablo")} ${import_picocolors6.default.dim("sync engine \u2014 dev")} ${import_picocolors6.default.dim("(sandbox)")}
279877
280057
  `);
280058
+ const projectDbUrl = readProjectDatabaseUrl();
280059
+ if (projectDbUrl) await ensureScopedRoleInteractive(projectDbUrl);
279878
280060
  const schema = await loadSchema(args.schemaPath, args.exportName);
279879
280061
  const modelCount = Object.keys(schema.models).length;
279880
280062
  console.log(
279881
- ` ${import_picocolors5.default.dim("schema")} ${import_picocolors5.default.bold(args.schemaPath)} ${import_picocolors5.default.dim(`(${modelCount} models, hash ${(0, import_schema4.schemaHash)(schema)})`)}`
280063
+ ` ${import_picocolors6.default.dim("schema")} ${import_picocolors6.default.bold(args.schemaPath)} ${import_picocolors6.default.dim(`(${modelCount} models, hash ${(0, import_schema4.schemaHash)(schema)})`)}`
279882
280064
  );
279883
- console.log(` ${import_picocolors5.default.dim("key")} ${args.apiKey.slice(0, 12)}\u2026`);
279884
- console.log(` ${import_picocolors5.default.dim("api")} ${args.url}
280065
+ console.log(` ${import_picocolors6.default.dim("key")} ${args.apiKey.slice(0, 12)}\u2026`);
280066
+ console.log(` ${import_picocolors6.default.dim("api")} ${args.url}
279885
280067
  `);
279886
280068
  const s = Y2();
279887
280069
  s.start("Pushing schema definition (sandbox)");
@@ -279890,20 +280072,20 @@ async function dev(argv) {
279890
280072
  if (!first.ok) process.exit(1);
279891
280073
  if (process.env.ABLO_API_KEY) {
279892
280074
  console.log(`
279893
- ${import_picocolors5.default.green("\u2713")} ${import_picocolors5.default.bold("ABLO_API_KEY")} is set in this shell \u2014 the SDK reads it directly.`);
280075
+ ${import_picocolors6.default.green("\u2713")} ${import_picocolors6.default.bold("ABLO_API_KEY")} is set in this shell \u2014 the SDK reads it directly.`);
279894
280076
  } else {
279895
280077
  console.log(`
279896
- ${import_picocolors5.default.green("\u2713")} ${wireEnvLocal(args.apiKey)}`);
279897
- console.log(` ${import_picocolors5.default.dim("Frameworks load it automatically; plain Node: node --env-file=.env.local app.ts")}`);
280078
+ ${import_picocolors6.default.green("\u2713")} ${wireEnvLocal(args.apiKey)}`);
280079
+ console.log(` ${import_picocolors6.default.dim("Frameworks load it automatically; plain Node: node --env-file=.env.local app.ts")}`);
279898
280080
  }
279899
280081
  console.log(` Your app is wired for the sandbox.`);
279900
280082
  if (!args.watch) return;
279901
- const abs = (0, import_path4.resolve)(process.cwd(), args.schemaPath);
279902
- console.log(` ${import_picocolors5.default.dim(`watching ${args.schemaPath} \u2026 (Ctrl-C to stop)`)}
280083
+ const abs = (0, import_path5.resolve)(process.cwd(), args.schemaPath);
280084
+ console.log(` ${import_picocolors6.default.dim(`watching ${args.schemaPath} \u2026 (Ctrl-C to stop)`)}
279903
280085
  `);
279904
280086
  let timer2 = null;
279905
280087
  let pushing = false;
279906
- const watcher = (0, import_fs6.watch)(abs, () => {
280088
+ const watcher = (0, import_fs7.watch)(abs, () => {
279907
280089
  if (timer2) clearTimeout(timer2);
279908
280090
  timer2 = setTimeout(() => {
279909
280091
  void rePush();
@@ -279919,7 +280101,7 @@ async function dev(argv) {
279919
280101
  const r2 = await runPush(next, args);
279920
280102
  s2.stop(r2.message, r2.ok ? 0 : 1);
279921
280103
  } catch (err) {
279922
- s2.stop(import_picocolors5.default.red(`schema reload failed: ${err instanceof Error ? err.message : String(err)}`), 1);
280104
+ s2.stop(import_picocolors6.default.red(`schema reload failed: ${err instanceof Error ? err.message : String(err)}`), 1);
279923
280105
  } finally {
279924
280106
  pushing = false;
279925
280107
  }
@@ -279927,7 +280109,7 @@ async function dev(argv) {
279927
280109
  const stop = () => {
279928
280110
  watcher.close();
279929
280111
  console.log(`
279930
- ${import_picocolors5.default.dim("stopped.")}`);
280112
+ ${import_picocolors6.default.dim("stopped.")}`);
279931
280113
  process.exit(0);
279932
280114
  };
279933
280115
  process.on("SIGINT", stop);
@@ -279939,7 +280121,7 @@ async function dev(argv) {
279939
280121
  // src/cli/login.ts
279940
280122
  init_cjs_shims();
279941
280123
  var import_child_process = require("child_process");
279942
- var import_picocolors6 = __toESM(require_picocolors(), 1);
280124
+ var import_picocolors7 = __toESM(require_picocolors(), 1);
279943
280125
  var CLIENT_ID = "ablo-cli";
279944
280126
  var AUTH_URL = (process.env.ABLO_AUTH_URL ?? "https://www.abloatai.com").replace(/\/+$/, "");
279945
280127
  var sleep = (ms) => new Promise((r2) => setTimeout(r2, ms));
@@ -279955,16 +280137,21 @@ function openBrowser(url) {
279955
280137
  }
279956
280138
  async function deviceLogin() {
279957
280139
  Ie(`${brand("ablo")} login`);
279958
- const account = await ve({
279959
- message: "Ablo account",
279960
- options: [
279961
- { value: "login", label: "Log in to an existing account" },
279962
- { value: "signup", label: "Create a new account" }
279963
- ]
279964
- });
279965
- if (pD(account)) {
279966
- xe("Cancelled.");
279967
- process.exit(0);
280140
+ const interactive = Boolean(process.stdout.isTTY && process.stdin.isTTY);
280141
+ let account = "login";
280142
+ if (interactive) {
280143
+ const choice = await ve({
280144
+ message: "Ablo account",
280145
+ options: [
280146
+ { value: "login", label: "Log in to an existing account" },
280147
+ { value: "signup", label: "Create a new account" }
280148
+ ]
280149
+ });
280150
+ if (pD(choice)) {
280151
+ xe("Cancelled.");
280152
+ process.exit(0);
280153
+ }
280154
+ account = choice;
279968
280155
  }
279969
280156
  const codeRes = await fetch(`${AUTH_URL}/api/auth/device/code`, {
279970
280157
  method: "POST",
@@ -279978,9 +280165,9 @@ async function deviceLogin() {
279978
280165
  const code = await codeRes.json();
279979
280166
  const approvePath = `/cli?user_code=${code.user_code}`;
279980
280167
  const url = account === "signup" ? `${AUTH_URL}/signup?next=${encodeURIComponent(approvePath)}` : code.verification_uri_complete ?? code.verification_uri;
279981
- Me(`${import_picocolors6.default.bold(code.user_code)}
280168
+ Me(`${import_picocolors7.default.bold(code.user_code)}
279982
280169
 
279983
- ${import_picocolors6.default.dim(url)}`, "Approve in your browser");
280170
+ ${import_picocolors7.default.dim(url)}`, "Approve in your browser");
279984
280171
  openBrowser(url);
279985
280172
  const s = Y2();
279986
280173
  s.start("Waiting for approval\u2026");
@@ -280038,7 +280225,7 @@ ${import_picocolors6.default.dim(url)}`, "Approve in your browser");
280038
280225
  if (reason) M2.error(reason);
280039
280226
  else if (provRes) M2.error(`Key provisioning returned ${provRes.status} from ${AUTH_URL}/api/cli/provision-key.`);
280040
280227
  M2.error(
280041
- `The browser approval succeeded but the key handoff failed. Try again, or grab a ${import_picocolors6.default.bold("sk_test_")} key from the dashboard and set ${import_picocolors6.default.bold("ABLO_API_KEY")}.`
280228
+ `The browser approval succeeded but the key handoff failed. Try again, or grab a ${import_picocolors7.default.bold("sk_test_")} key from the dashboard and set ${import_picocolors7.default.bold("ABLO_API_KEY")}.`
280042
280229
  );
280043
280230
  process.exit(1);
280044
280231
  }
@@ -280054,7 +280241,7 @@ ${import_picocolors6.default.dim(url)}`, "Approve in your browser");
280054
280241
  ...prov.live ? { production: entry(prov.live) } : {}
280055
280242
  });
280056
280243
  s.stop(`Saved keys to ${path}`);
280057
- Se(`${import_picocolors6.default.green("\u2713")} Logged in ${import_picocolors6.default.dim("(sandbox)")}. Run ${import_picocolors6.default.bold("ablo dev")}, or ${import_picocolors6.default.bold("ablo mode production")} to switch.`);
280244
+ Se(`${import_picocolors7.default.green("\u2713")} Logged in ${import_picocolors7.default.dim("(sandbox)")}. Run ${import_picocolors7.default.bold("ablo dev")}, or ${import_picocolors7.default.bold("ablo mode production")} to switch.`);
280058
280245
  }
280059
280246
  async function login() {
280060
280247
  await deviceLogin();
@@ -280062,20 +280249,20 @@ async function login() {
280062
280249
  function logout() {
280063
280250
  const removed = clearCredential();
280064
280251
  if (removed) {
280065
- console.log(` ${import_picocolors6.default.green("\u2713")} Logged out ${import_picocolors6.default.dim(`(credentials removed from ${configDir()})`)}`);
280252
+ console.log(` ${import_picocolors7.default.green("\u2713")} Logged out ${import_picocolors7.default.dim(`(credentials removed from ${configDir()})`)}`);
280066
280253
  } else {
280067
- console.log(` ${import_picocolors6.default.dim("\u25CB")} Not logged in \u2014 nothing to remove.`);
280254
+ console.log(` ${import_picocolors7.default.dim("\u25CB")} Not logged in \u2014 nothing to remove.`);
280068
280255
  }
280069
280256
  if (process.env.ABLO_API_KEY) {
280070
280257
  console.log(
280071
- import_picocolors6.default.dim(` Note: ${import_picocolors6.default.bold("ABLO_API_KEY")} is still set in this shell and takes precedence.`)
280258
+ import_picocolors7.default.dim(` Note: ${import_picocolors7.default.bold("ABLO_API_KEY")} is still set in this shell and takes precedence.`)
280072
280259
  );
280073
280260
  }
280074
280261
  }
280075
280262
 
280076
280263
  // src/cli/mode.ts
280077
280264
  init_cjs_shims();
280078
- var import_picocolors7 = __toESM(require_picocolors(), 1);
280265
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
280079
280266
  var PREFIX = { sandbox: "sk_test_", production: "rk_live_" };
280080
280267
  function hintFor(m2, current) {
280081
280268
  const parts = [];
@@ -280085,10 +280272,10 @@ function hintFor(m2, current) {
280085
280272
  }
280086
280273
  function apply(m2) {
280087
280274
  setMode(m2);
280088
- console.log(` ${import_picocolors7.default.green("\u2713")} now in ${import_picocolors7.default.bold(m2)}`);
280275
+ console.log(` ${import_picocolors8.default.green("\u2713")} now in ${import_picocolors8.default.bold(m2)}`);
280089
280276
  if (!getKeyEntry(m2)) {
280090
280277
  console.log(
280091
- import_picocolors7.default.dim(` No ${m2} key stored \u2014 run ${import_picocolors7.default.bold("ablo login")} or ${import_picocolors7.default.bold(`ablo login --api-key ${PREFIX[m2]}\u2026`)}.`)
280278
+ import_picocolors8.default.dim(` No ${m2} key stored \u2014 run ${import_picocolors8.default.bold("ablo login")} or ${import_picocolors8.default.bold(`ablo login --api-key ${PREFIX[m2]}\u2026`)}.`)
280092
280279
  );
280093
280280
  }
280094
280281
  }
@@ -280101,14 +280288,14 @@ async function mode(argv) {
280101
280288
  }
280102
280289
  if (arg) {
280103
280290
  console.error(
280104
- import_picocolors7.default.red(` unknown mode: ${arg}`) + import_picocolors7.default.dim(` (expected ${import_picocolors7.default.bold("sandbox")} or ${import_picocolors7.default.bold("production")})`)
280291
+ import_picocolors8.default.red(` unknown mode: ${arg}`) + import_picocolors8.default.dim(` (expected ${import_picocolors8.default.bold("sandbox")} or ${import_picocolors8.default.bold("production")})`)
280105
280292
  );
280106
280293
  process.exit(1);
280107
280294
  }
280108
280295
  const current = getMode();
280109
280296
  if (!process.stdin.isTTY || process.env.CI) {
280110
280297
  console.error(
280111
- import_picocolors7.default.red(" `ablo mode` needs an argument without a TTY: ") + import_picocolors7.default.bold("ablo mode sandbox") + import_picocolors7.default.dim(" | ") + import_picocolors7.default.bold("ablo mode production") + import_picocolors7.default.dim(` (current: ${current})`)
280298
+ import_picocolors8.default.red(" `ablo mode` needs an argument without a TTY: ") + import_picocolors8.default.bold("ablo mode sandbox") + import_picocolors8.default.dim(" | ") + import_picocolors8.default.bold("ablo mode production") + import_picocolors8.default.dim(` (current: ${current})`)
280112
280299
  );
280113
280300
  process.exit(1);
280114
280301
  }
@@ -280129,13 +280316,13 @@ async function mode(argv) {
280129
280316
 
280130
280317
  // src/cli/status.ts
280131
280318
  init_cjs_shims();
280132
- var import_picocolors8 = __toESM(require_picocolors(), 1);
280319
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
280133
280320
  function expiryLabel(iso) {
280134
280321
  const ms = Date.parse(iso) - Date.now();
280135
280322
  if (Number.isNaN(ms)) return "";
280136
- if (ms <= 0) return import_picocolors8.default.red("expired");
280323
+ if (ms <= 0) return import_picocolors9.default.red("expired");
280137
280324
  const days = Math.floor(ms / (24 * 60 * 60 * 1e3));
280138
- return import_picocolors8.default.dim(days > 0 ? `expires in ${days}d` : "expires <1d");
280325
+ return import_picocolors9.default.dim(days > 0 ? `expires in ${days}d` : "expires <1d");
280139
280326
  }
280140
280327
  async function ping(apiUrl) {
280141
280328
  const ctrl = new AbortController();
@@ -280154,36 +280341,36 @@ async function status() {
280154
280341
  const cfg = readConfig();
280155
280342
  const mode2 = getMode();
280156
280343
  console.log(`
280157
- ${brand("ablo")} ${import_picocolors8.default.dim("status")}
280344
+ ${brand("ablo")} ${import_picocolors9.default.dim("status")}
280158
280345
  `);
280159
280346
  if (process.env.ABLO_API_KEY) {
280160
280347
  console.log(
280161
- ` ${import_picocolors8.default.dim("key")} ${process.env.ABLO_API_KEY.slice(0, 12)}\u2026 ${import_picocolors8.default.dim("(ABLO_API_KEY env \u2014 overrides stored)")}`
280348
+ ` ${import_picocolors9.default.dim("key")} ${process.env.ABLO_API_KEY.slice(0, 12)}\u2026 ${import_picocolors9.default.dim("(ABLO_API_KEY env \u2014 overrides stored)")}`
280162
280349
  );
280163
280350
  } else if (!cfg) {
280164
- console.log(` ${import_picocolors8.default.yellow("!")} Not logged in \u2014 run ${import_picocolors8.default.bold("ablo login")}.`);
280351
+ console.log(` ${import_picocolors9.default.yellow("!")} Not logged in \u2014 run ${import_picocolors9.default.bold("ablo login")}.`);
280165
280352
  }
280166
- console.log(` ${import_picocolors8.default.dim("mode")} ${import_picocolors8.default.bold(mode2)}`);
280353
+ console.log(` ${import_picocolors9.default.dim("mode")} ${import_picocolors9.default.bold(mode2)}`);
280167
280354
  for (const m2 of ["sandbox", "production"]) {
280168
280355
  const entry = getKeyEntry(m2);
280169
- const marker = m2 === mode2 ? import_picocolors8.default.green("\u25CF") : import_picocolors8.default.dim("\u25CB");
280356
+ const marker = m2 === mode2 ? import_picocolors9.default.green("\u25CF") : import_picocolors9.default.dim("\u25CB");
280170
280357
  if (entry) {
280171
280358
  const exp = entry.expiresAt ? ` ${expiryLabel(entry.expiresAt)}` : "";
280172
- console.log(` ${marker} ${m2.padEnd(10)} ${import_picocolors8.default.dim(`${entry.apiKey.slice(0, 12)}\u2026`)}${exp}`);
280359
+ console.log(` ${marker} ${m2.padEnd(10)} ${import_picocolors9.default.dim(`${entry.apiKey.slice(0, 12)}\u2026`)}${exp}`);
280173
280360
  } else {
280174
- console.log(` ${marker} ${m2.padEnd(10)} ${import_picocolors8.default.dim("\u2014 no key")}`);
280361
+ console.log(` ${marker} ${m2.padEnd(10)} ${import_picocolors9.default.dim("\u2014 no key")}`);
280175
280362
  }
280176
280363
  }
280177
280364
  const org = getKeyEntry(mode2)?.organizationId;
280178
- if (org) console.log(` ${import_picocolors8.default.dim("org")} ${org}`);
280179
- process.stdout.write(` ${import_picocolors8.default.dim("api")} ${apiUrl} `);
280180
- console.log(await ping(apiUrl) ? import_picocolors8.default.green("reachable") : import_picocolors8.default.red("unreachable"));
280365
+ if (org) console.log(` ${import_picocolors9.default.dim("org")} ${org}`);
280366
+ process.stdout.write(` ${import_picocolors9.default.dim("api")} ${apiUrl} `);
280367
+ console.log(await ping(apiUrl) ? import_picocolors9.default.green("reachable") : import_picocolors9.default.red("unreachable"));
280181
280368
  console.log();
280182
280369
  }
280183
280370
 
280184
280371
  // src/cli/logs.ts
280185
280372
  init_cjs_shims();
280186
- var import_picocolors9 = __toESM(require_picocolors(), 1);
280373
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
280187
280374
  function parseLogsArgs(argv) {
280188
280375
  const args = {
280189
280376
  follow: true,
@@ -280247,10 +280434,10 @@ function resolveSince(since) {
280247
280434
  var sleep2 = (ms) => new Promise((r2) => setTimeout(r2, ms));
280248
280435
  function colorOp(op) {
280249
280436
  const label = op.padEnd(6);
280250
- if (op === "create") return import_picocolors9.default.green(label);
280251
- if (op === "update") return import_picocolors9.default.yellow(label);
280252
- if (op === "delete") return import_picocolors9.default.red(label);
280253
- return import_picocolors9.default.dim(label);
280437
+ if (op === "create") return import_picocolors10.default.green(label);
280438
+ if (op === "update") return import_picocolors10.default.yellow(label);
280439
+ if (op === "delete") return import_picocolors10.default.red(label);
280440
+ return import_picocolors10.default.dim(label);
280254
280441
  }
280255
280442
  function render(e2, json) {
280256
280443
  if (json) {
@@ -280259,21 +280446,21 @@ function render(e2, json) {
280259
280446
  return;
280260
280447
  }
280261
280448
  const t = new Date(e2.at).toLocaleTimeString();
280262
- const actor = e2.actor ? import_picocolors9.default.dim(` ${e2.actor}`) : "";
280263
- console.log(` ${import_picocolors9.default.dim(t)} ${colorOp(e2.op)} ${import_picocolors9.default.bold(e2.model)} ${import_picocolors9.default.dim(e2.recordId)}${actor}`);
280449
+ const actor = e2.actor ? import_picocolors10.default.dim(` ${e2.actor}`) : "";
280450
+ console.log(` ${import_picocolors10.default.dim(t)} ${colorOp(e2.op)} ${import_picocolors10.default.bold(e2.model)} ${import_picocolors10.default.dim(e2.recordId)}${actor}`);
280264
280451
  }
280265
280452
  async function logs(argv) {
280266
280453
  let args;
280267
280454
  try {
280268
280455
  args = parseLogsArgs(argv);
280269
280456
  } catch (err) {
280270
- console.error(import_picocolors9.default.red(` ${err instanceof Error ? err.message : String(err)}`));
280457
+ console.error(import_picocolors10.default.red(` ${err instanceof Error ? err.message : String(err)}`));
280271
280458
  process.exit(1);
280272
280459
  }
280273
280460
  const apiKey = resolveApiKey(args.mode);
280274
280461
  if (!apiKey) {
280275
280462
  console.error(
280276
- import_picocolors9.default.red(` No API key.`) + import_picocolors9.default.dim(` Run ${import_picocolors9.default.bold("ablo login")} or set ${import_picocolors9.default.bold("ABLO_API_KEY")}.`)
280463
+ import_picocolors10.default.red(` No API key.`) + import_picocolors10.default.dim(` Run ${import_picocolors10.default.bold("ablo login")} or set ${import_picocolors10.default.bold("ABLO_API_KEY")}.`)
280277
280464
  );
280278
280465
  process.exit(1);
280279
280466
  }
@@ -280287,7 +280474,7 @@ async function logs(argv) {
280287
280474
  if (!res) return null;
280288
280475
  if (!res.ok) {
280289
280476
  const body = await res.json().catch(() => ({}));
280290
- console.error(import_picocolors9.default.red(` logs failed (${res.status}): ${body.reason ?? body.message ?? ""}`));
280477
+ console.error(import_picocolors10.default.red(` logs failed (${res.status}): ${body.reason ?? body.message ?? ""}`));
280291
280478
  process.exit(1);
280292
280479
  }
280293
280480
  const json = await res.json();
@@ -280298,7 +280485,7 @@ async function logs(argv) {
280298
280485
  }
280299
280486
  if (!args.json) {
280300
280487
  console.log(`
280301
- ${brand("ablo")} ${import_picocolors9.default.dim("logs")} ${import_picocolors9.default.dim(`(${args.mode ?? "active"} mode)`)}
280488
+ ${brand("ablo")} ${import_picocolors10.default.dim("logs")} ${import_picocolors10.default.dim(`(${args.mode ?? "active"} mode)`)}
280302
280489
  `);
280303
280490
  }
280304
280491
  const initial = await fetchPage({
@@ -280308,13 +280495,13 @@ async function logs(argv) {
280308
280495
  ...args.op ? { op: args.op } : {}
280309
280496
  });
280310
280497
  if (!initial) {
280311
- console.error(import_picocolors9.default.red(` Couldn't reach ${baseUrl2}.`));
280498
+ console.error(import_picocolors10.default.red(` Couldn't reach ${baseUrl2}.`));
280312
280499
  process.exit(1);
280313
280500
  }
280314
280501
  for (const e2 of initial.events) render(e2, args.json);
280315
280502
  let cursor = initial.cursor;
280316
280503
  if (!args.follow) return;
280317
- if (!args.json) console.log(` ${import_picocolors9.default.dim("watching for new activity \u2026 (Ctrl-C to stop)")}
280504
+ if (!args.json) console.log(` ${import_picocolors10.default.dim("watching for new activity \u2026 (Ctrl-C to stop)")}
280318
280505
  `);
280319
280506
  for (; ; ) {
280320
280507
  await sleep2(1500);
@@ -280331,8 +280518,8 @@ async function logs(argv) {
280331
280518
 
280332
280519
  // src/cli/webhooks.ts
280333
280520
  init_cjs_shims();
280334
- var import_fs7 = require("fs");
280335
- var import_picocolors10 = __toESM(require_picocolors(), 1);
280521
+ var import_fs8 = require("fs");
280522
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
280336
280523
  var ENV_KEY = "ABLO_WEBHOOK_SECRET";
280337
280524
  function flag(args, name) {
280338
280525
  const inline = args.find((a) => a.startsWith(`${name}=`));
@@ -280359,12 +280546,12 @@ function requireKey(mode2) {
280359
280546
  const apiKey = resolveApiKey(mode2);
280360
280547
  if (!apiKey) {
280361
280548
  console.error(
280362
- import_picocolors10.default.red(" No API key.") + import_picocolors10.default.dim(` Run ${import_picocolors10.default.bold("ablo login")} or set ${import_picocolors10.default.bold("ABLO_API_KEY")}.`)
280549
+ import_picocolors11.default.red(" No API key.") + import_picocolors11.default.dim(` Run ${import_picocolors11.default.bold("ablo login")} or set ${import_picocolors11.default.bold("ABLO_API_KEY")}.`)
280363
280550
  );
280364
280551
  process.exit(1);
280365
280552
  }
280366
280553
  if (!apiKey.startsWith("sk_")) {
280367
- console.error(import_picocolors10.default.red(" Managing webhooks requires a secret key ") + import_picocolors10.default.dim("(sk_test_ / sk_live_)."));
280554
+ console.error(import_picocolors11.default.red(" Managing webhooks requires a secret key ") + import_picocolors11.default.dim("(sk_test_ / sk_live_)."));
280368
280555
  process.exit(1);
280369
280556
  }
280370
280557
  return apiKey;
@@ -280380,22 +280567,22 @@ async function api(apiKey, method, path, body) {
280380
280567
  ...body ? { body: JSON.stringify(body) } : {}
280381
280568
  }).catch(() => null);
280382
280569
  if (!res) {
280383
- console.error(import_picocolors10.default.red(` Couldn't reach ${baseUrl()}.`));
280570
+ console.error(import_picocolors11.default.red(` Couldn't reach ${baseUrl()}.`));
280384
280571
  process.exit(1);
280385
280572
  }
280386
280573
  if (!res.ok) {
280387
280574
  const err = await res.json().catch(() => ({}));
280388
- console.error(import_picocolors10.default.red(` Request failed (${res.status}): ${err.message ?? err.reason ?? ""}`));
280575
+ console.error(import_picocolors11.default.red(` Request failed (${res.status}): ${err.message ?? err.reason ?? ""}`));
280389
280576
  process.exit(1);
280390
280577
  }
280391
280578
  return await res.json();
280392
280579
  }
280393
280580
  function writeSecretToEnv(secret) {
280394
- const file = (0, import_fs7.existsSync)(".env.local") ? ".env.local" : (0, import_fs7.existsSync)(".env") ? ".env" : ".env.local";
280581
+ const file = (0, import_fs8.existsSync)(".env.local") ? ".env.local" : (0, import_fs8.existsSync)(".env") ? ".env" : ".env.local";
280395
280582
  const line = `${ENV_KEY}=${secret}`;
280396
280583
  let next;
280397
- if ((0, import_fs7.existsSync)(file)) {
280398
- const existing = (0, import_fs7.readFileSync)(file, "utf-8");
280584
+ if ((0, import_fs8.existsSync)(file)) {
280585
+ const existing = (0, import_fs8.readFileSync)(file, "utf-8");
280399
280586
  next = new RegExp(`^${ENV_KEY}=.*$`, "m").test(existing) ? existing.replace(new RegExp(`^${ENV_KEY}=.*$`, "m"), line) : `${existing.replace(/\n*$/, "")}
280400
280587
  ${line}
280401
280588
  `;
@@ -280403,15 +280590,15 @@ ${line}
280403
280590
  next = `${line}
280404
280591
  `;
280405
280592
  }
280406
- (0, import_fs7.writeFileSync)(file, next);
280593
+ (0, import_fs8.writeFileSync)(file, next);
280407
280594
  return file;
280408
280595
  }
280409
280596
  function printEndpoint(e2) {
280410
- const dot = e2.status === "enabled" ? import_picocolors10.default.green("\u25CF") : import_picocolors10.default.red("\u25CF");
280411
- const health = e2.last_error ? import_picocolors10.default.red(` last error: ${e2.last_error}`) : "";
280412
- console.log(` ${dot} ${import_picocolors10.default.bold(e2.id)} ${e2.url}`);
280597
+ const dot = e2.status === "enabled" ? import_picocolors11.default.green("\u25CF") : import_picocolors11.default.red("\u25CF");
280598
+ const health = e2.last_error ? import_picocolors11.default.red(` last error: ${e2.last_error}`) : "";
280599
+ console.log(` ${dot} ${import_picocolors11.default.bold(e2.id)} ${e2.url}`);
280413
280600
  console.log(
280414
- import_picocolors10.default.dim(
280601
+ import_picocolors11.default.dim(
280415
280602
  ` ${e2.status} \xB7 ${e2.environment} \xB7 events ${e2.enabled_events.join(",")} \xB7 cursor ${e2.cursor ?? "\u2014"}${health}`
280416
280603
  )
280417
280604
  );
@@ -280423,7 +280610,7 @@ async function webhooks(argv) {
280423
280610
  if (sub === "create") {
280424
280611
  const url = positional(rest);
280425
280612
  if (!url) {
280426
- console.error(import_picocolors10.default.red(" Usage: ") + brand("ablo webhooks create <url>"));
280613
+ console.error(import_picocolors11.default.red(" Usage: ") + brand("ablo webhooks create <url>"));
280427
280614
  process.exit(1);
280428
280615
  }
280429
280616
  const apiKey = requireKey(mode2);
@@ -280435,8 +280622,8 @@ async function webhooks(argv) {
280435
280622
  });
280436
280623
  const file = writeSecretToEnv(created.secret);
280437
280624
  console.log(`
280438
- ${import_picocolors10.default.green("\u2713")} Registered ${import_picocolors10.default.bold(created.id)} \u2192 ${created.url}`);
280439
- console.log(` ${import_picocolors10.default.green("\u2713")} Wrote ${import_picocolors10.default.bold(ENV_KEY)} to ${import_picocolors10.default.bold(file)} ${import_picocolors10.default.dim("(shown once)")}
280625
+ ${import_picocolors11.default.green("\u2713")} Registered ${import_picocolors11.default.bold(created.id)} \u2192 ${created.url}`);
280626
+ console.log(` ${import_picocolors11.default.green("\u2713")} Wrote ${import_picocolors11.default.bold(ENV_KEY)} to ${import_picocolors11.default.bold(file)} ${import_picocolors11.default.dim("(shown once)")}
280440
280627
  `);
280441
280628
  return;
280442
280629
  }
@@ -280444,7 +280631,7 @@ async function webhooks(argv) {
280444
280631
  const apiKey = requireKey(mode2);
280445
280632
  const { data } = await api(apiKey, "GET", "");
280446
280633
  if (data.length === 0) {
280447
- console.log(import_picocolors10.default.dim(" No webhook endpoints. ") + brand("ablo webhooks create <url>"));
280634
+ console.log(import_picocolors11.default.dim(" No webhook endpoints. ") + brand("ablo webhooks create <url>"));
280448
280635
  return;
280449
280636
  }
280450
280637
  console.log();
@@ -280455,40 +280642,40 @@ async function webhooks(argv) {
280455
280642
  if (sub === "roll") {
280456
280643
  const id = positional(rest);
280457
280644
  if (!id) {
280458
- console.error(import_picocolors10.default.red(" Usage: ") + brand("ablo webhooks roll <id>"));
280645
+ console.error(import_picocolors11.default.red(" Usage: ") + brand("ablo webhooks roll <id>"));
280459
280646
  process.exit(1);
280460
280647
  }
280461
280648
  const apiKey = requireKey(mode2);
280462
280649
  const rolled = await api(apiKey, "POST", `/${id}/roll_secret`);
280463
280650
  const file = writeSecretToEnv(rolled.secret);
280464
280651
  console.log(`
280465
- ${import_picocolors10.default.green("\u2713")} Rolled secret for ${import_picocolors10.default.bold(id)} \u2192 ${import_picocolors10.default.bold(file)} ${import_picocolors10.default.dim("(old secret now invalid)")}
280652
+ ${import_picocolors11.default.green("\u2713")} Rolled secret for ${import_picocolors11.default.bold(id)} \u2192 ${import_picocolors11.default.bold(file)} ${import_picocolors11.default.dim("(old secret now invalid)")}
280466
280653
  `);
280467
280654
  return;
280468
280655
  }
280469
280656
  if (sub === "enable") {
280470
280657
  const id = positional(rest);
280471
280658
  if (!id) {
280472
- console.error(import_picocolors10.default.red(" Usage: ") + brand("ablo webhooks enable <id>"));
280659
+ console.error(import_picocolors11.default.red(" Usage: ") + brand("ablo webhooks enable <id>"));
280473
280660
  process.exit(1);
280474
280661
  }
280475
280662
  const apiKey = requireKey(mode2);
280476
280663
  const e2 = await api(apiKey, "POST", `/${id}/enable`);
280477
- console.log(` ${import_picocolors10.default.green("\u2713")} Re-enabled ${import_picocolors10.default.bold(e2.id)}`);
280664
+ console.log(` ${import_picocolors11.default.green("\u2713")} Re-enabled ${import_picocolors11.default.bold(e2.id)}`);
280478
280665
  return;
280479
280666
  }
280480
280667
  if (sub === "rm" || sub === "delete") {
280481
280668
  const id = positional(rest);
280482
280669
  if (!id) {
280483
- console.error(import_picocolors10.default.red(" Usage: ") + brand("ablo webhooks rm <id>"));
280670
+ console.error(import_picocolors11.default.red(" Usage: ") + brand("ablo webhooks rm <id>"));
280484
280671
  process.exit(1);
280485
280672
  }
280486
280673
  const apiKey = requireKey(mode2);
280487
280674
  await api(apiKey, "DELETE", `/${id}`);
280488
- console.log(` ${import_picocolors10.default.green("\u2713")} Removed ${import_picocolors10.default.bold(id)}`);
280675
+ console.log(` ${import_picocolors11.default.green("\u2713")} Removed ${import_picocolors11.default.bold(id)}`);
280489
280676
  return;
280490
280677
  }
280491
- console.log(` ${import_picocolors10.default.bold("Usage:")}`);
280678
+ console.log(` ${import_picocolors11.default.bold("Usage:")}`);
280492
280679
  console.log(` ${brand("ablo webhooks create <url>")} Register an endpoint; writes ${ENV_KEY}`);
280493
280680
  console.log(` ${brand("ablo webhooks list")} List endpoints + delivery health`);
280494
280681
  console.log(` ${brand("ablo webhooks roll <id>")} Mint a fresh signing secret`);
@@ -280499,7 +280686,7 @@ async function webhooks(argv) {
280499
280686
 
280500
280687
  // src/cli/check.ts
280501
280688
  init_cjs_shims();
280502
- var import_picocolors11 = __toESM(require_picocolors(), 1);
280689
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
280503
280690
  var import_schema5 = require("@abloatai/ablo/schema");
280504
280691
  var DEFAULT_SCHEMA_PATH4 = "ablo/schema.ts";
280505
280692
  var DEFAULT_EXPORT4 = "schema";
@@ -280534,13 +280721,13 @@ async function check(argv) {
280534
280721
  try {
280535
280722
  args = parseCheckArgs(argv);
280536
280723
  } catch (err) {
280537
- console.error(import_picocolors11.default.red(` ${err instanceof Error ? err.message : String(err)}`));
280724
+ console.error(import_picocolors12.default.red(` ${err instanceof Error ? err.message : String(err)}`));
280538
280725
  process.exit(1);
280539
280726
  }
280540
280727
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
280541
280728
  if (!dbUrl) {
280542
280729
  console.error(
280543
- import_picocolors11.default.red(` No database.`) + import_picocolors11.default.dim(` Set ${import_picocolors11.default.bold("DATABASE_URL")} to the Postgres you want Ablo to adopt.`)
280730
+ import_picocolors12.default.red(` No database.`) + import_picocolors12.default.dim(` Set ${import_picocolors12.default.bold("DATABASE_URL")} to the Postgres you want Ablo to adopt.`)
280544
280731
  );
280545
280732
  process.exit(1);
280546
280733
  }
@@ -280555,7 +280742,7 @@ async function check(argv) {
280555
280742
  [args.appSchema]
280556
280743
  );
280557
280744
  } catch (err) {
280558
- console.error(import_picocolors11.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
280745
+ console.error(import_picocolors12.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
280559
280746
  await sql.end({ timeout: 2 });
280560
280747
  process.exit(1);
280561
280748
  }
@@ -280570,7 +280757,7 @@ async function check(argv) {
280570
280757
  set.add(r2.column_name);
280571
280758
  }
280572
280759
  console.log(`
280573
- ${brand("ablo")} ${import_picocolors11.default.dim("check")} ${import_picocolors11.default.dim(`schema "${args.appSchema}"`)}
280760
+ ${brand("ablo")} ${import_picocolors12.default.dim("check")} ${import_picocolors12.default.dim(`schema "${args.appSchema}"`)}
280574
280761
  `);
280575
280762
  const declaredTables = /* @__PURE__ */ new Set();
280576
280763
  let errors = 0;
@@ -280580,7 +280767,7 @@ async function check(argv) {
280580
280767
  declaredTables.add(table);
280581
280768
  const present = colsByTable.get(table);
280582
280769
  if (!present) {
280583
- console.log(` ${import_picocolors11.default.red("\u2717")} ${import_picocolors11.default.bold(key)} ${import_picocolors11.default.dim("\u2192")} table ${import_picocolors11.default.bold(table)} ${import_picocolors11.default.red("not found")}`);
280770
+ console.log(` ${import_picocolors12.default.red("\u2717")} ${import_picocolors12.default.bold(key)} ${import_picocolors12.default.dim("\u2192")} table ${import_picocolors12.default.bold(table)} ${import_picocolors12.default.red("not found")}`);
280584
280771
  errors++;
280585
280772
  continue;
280586
280773
  }
@@ -280602,26 +280789,26 @@ async function check(argv) {
280602
280789
  if (!present.has(col)) problems.push(`missing column "${col}" (field ${fieldName})`);
280603
280790
  }
280604
280791
  if (problems.length > 0) {
280605
- console.log(` ${import_picocolors11.default.red("\u2717")} ${import_picocolors11.default.bold(key)} ${import_picocolors11.default.dim("\u2192")} ${table}`);
280606
- for (const p2 of problems) console.log(` ${import_picocolors11.default.red("\u2022")} ${p2}`);
280607
- for (const w2 of warns) console.log(` ${import_picocolors11.default.yellow("\u2022")} ${w2}`);
280792
+ console.log(` ${import_picocolors12.default.red("\u2717")} ${import_picocolors12.default.bold(key)} ${import_picocolors12.default.dim("\u2192")} ${table}`);
280793
+ for (const p2 of problems) console.log(` ${import_picocolors12.default.red("\u2022")} ${p2}`);
280794
+ for (const w2 of warns) console.log(` ${import_picocolors12.default.yellow("\u2022")} ${w2}`);
280608
280795
  errors++;
280609
280796
  } else if (warns.length > 0) {
280610
- console.log(` ${import_picocolors11.default.yellow("!")} ${import_picocolors11.default.bold(key)} ${import_picocolors11.default.dim("\u2192")} ${table}`);
280611
- for (const w2 of warns) console.log(` ${import_picocolors11.default.yellow("\u2022")} ${w2}`);
280797
+ console.log(` ${import_picocolors12.default.yellow("!")} ${import_picocolors12.default.bold(key)} ${import_picocolors12.default.dim("\u2192")} ${table}`);
280798
+ for (const w2 of warns) console.log(` ${import_picocolors12.default.yellow("\u2022")} ${w2}`);
280612
280799
  warnings++;
280613
280800
  } else {
280614
- console.log(` ${import_picocolors11.default.green("\u2713")} ${import_picocolors11.default.bold(key)} ${import_picocolors11.default.dim(`\u2192 ${table} (id, ${orgCol ?? "no org"} ok)`)}`);
280801
+ console.log(` ${import_picocolors12.default.green("\u2713")} ${import_picocolors12.default.bold(key)} ${import_picocolors12.default.dim(`\u2192 ${table} (id, ${orgCol ?? "no org"} ok)`)}`);
280615
280802
  }
280616
280803
  }
280617
280804
  const modelCount = Object.keys(schemaJson.models).length;
280618
280805
  const ignored = [...colsByTable.keys()].filter((t) => !declaredTables.has(t)).length;
280619
280806
  console.log(
280620
280807
  `
280621
- ${modelCount} model${modelCount === 1 ? "" : "s"} \xB7 ${import_picocolors11.default.green(`${modelCount - errors - warnings} ok`)}` + (warnings ? ` \xB7 ${import_picocolors11.default.yellow(`${warnings} warning${warnings === 1 ? "" : "s"}`)}` : "") + (errors ? ` \xB7 ${import_picocolors11.default.red(`${errors} error${errors === 1 ? "" : "s"}`)}` : "")
280808
+ ${modelCount} model${modelCount === 1 ? "" : "s"} \xB7 ${import_picocolors12.default.green(`${modelCount - errors - warnings} ok`)}` + (warnings ? ` \xB7 ${import_picocolors12.default.yellow(`${warnings} warning${warnings === 1 ? "" : "s"}`)}` : "") + (errors ? ` \xB7 ${import_picocolors12.default.red(`${errors} error${errors === 1 ? "" : "s"}`)}` : "")
280622
280809
  );
280623
280810
  if (ignored > 0) {
280624
- console.log(` ${import_picocolors11.default.dim(`${ignored} other table${ignored === 1 ? "" : "s"} in your database \u2014 ignored by Ablo`)}`);
280811
+ console.log(` ${import_picocolors12.default.dim(`${ignored} other table${ignored === 1 ? "" : "s"} in your database \u2014 ignored by Ablo`)}`);
280625
280812
  }
280626
280813
  console.log();
280627
280814
  process.exit(errors > 0 ? 1 : 0);
@@ -280629,7 +280816,7 @@ async function check(argv) {
280629
280816
 
280630
280817
  // src/cli/upgrade.ts
280631
280818
  init_cjs_shims();
280632
- var import_picocolors12 = __toESM(require_picocolors(), 1);
280819
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
280633
280820
  var import_ts_morph = __toESM(require_ts_morph(), 1);
280634
280821
  var DEFAULT_GLOBS = ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}", "ablo/**/*.{ts,tsx}", "lib/**/*.{ts,tsx}"];
280635
280822
  var VERB_ARGS = {
@@ -280707,7 +280894,7 @@ async function upgrade(argv) {
280707
280894
  project.addSourceFilesAtPaths(globs.length > 0 ? globs : DEFAULT_GLOBS);
280708
280895
  const files = project.getSourceFiles();
280709
280896
  if (files.length === 0) {
280710
- console.log(import_picocolors12.default.yellow(' No .ts/.tsx files found. Pass a glob, e.g. `ablo upgrade "src/**/*.tsx"`.'));
280897
+ console.log(import_picocolors13.default.yellow(' No .ts/.tsx files found. Pass a glob, e.g. `ablo upgrade "src/**/*.tsx"`.'));
280711
280898
  return;
280712
280899
  }
280713
280900
  const edits = [];
@@ -280781,39 +280968,39 @@ async function upgrade(argv) {
280781
280968
  const rel = (f) => f.replace(cwd + "/", "");
280782
280969
  console.log();
280783
280970
  if (edits.length === 0 && manual.length === 0) {
280784
- console.log(import_picocolors12.default.green(" \u2713 Nothing to migrate \u2014 your code is already on the current API."));
280971
+ console.log(import_picocolors13.default.green(" \u2713 Nothing to migrate \u2014 your code is already on the current API."));
280785
280972
  return;
280786
280973
  }
280787
280974
  if (edits.length > 0) {
280788
- console.log(import_picocolors12.default.bold(` ${write ? "Applied" : "Would apply"} ${edits.length} change${edits.length === 1 ? "" : "s"}:`));
280975
+ console.log(import_picocolors13.default.bold(` ${write ? "Applied" : "Would apply"} ${edits.length} change${edits.length === 1 ? "" : "s"}:`));
280789
280976
  for (const e2 of edits) {
280790
- console.log(` ${import_picocolors12.default.dim(`${rel(e2.file)}:${e2.line}`)} ${import_picocolors12.default.cyan(e2.rule)}`);
280791
- console.log(` ${import_picocolors12.default.red("-")} ${e2.before}`);
280792
- console.log(` ${import_picocolors12.default.green("+")} ${e2.after}`);
280977
+ console.log(` ${import_picocolors13.default.dim(`${rel(e2.file)}:${e2.line}`)} ${import_picocolors13.default.cyan(e2.rule)}`);
280978
+ console.log(` ${import_picocolors13.default.red("-")} ${e2.before}`);
280979
+ console.log(` ${import_picocolors13.default.green("+")} ${e2.after}`);
280793
280980
  }
280794
280981
  }
280795
280982
  if (manual.length > 0) {
280796
280983
  console.log();
280797
- console.log(import_picocolors12.default.bold(import_picocolors12.default.yellow(` ${manual.length} spot${manual.length === 1 ? "" : "s"} need manual review (structural):`)));
280984
+ console.log(import_picocolors13.default.bold(import_picocolors13.default.yellow(` ${manual.length} spot${manual.length === 1 ? "" : "s"} need manual review (structural):`)));
280798
280985
  for (const m2 of manual) {
280799
- console.log(` ${import_picocolors12.default.dim(`${rel(m2.file)}:${m2.line}`)} ${import_picocolors12.default.yellow(m2.rule)}`);
280800
- console.log(` ${import_picocolors12.default.dim(m2.snippet)}`);
280986
+ console.log(` ${import_picocolors13.default.dim(`${rel(m2.file)}:${m2.line}`)} ${import_picocolors13.default.yellow(m2.rule)}`);
280987
+ console.log(` ${import_picocolors13.default.dim(m2.snippet)}`);
280801
280988
  console.log(` \u2192 ${m2.hint}`);
280802
280989
  }
280803
280990
  }
280804
280991
  console.log();
280805
280992
  if (write) {
280806
280993
  await project.save();
280807
- console.log(import_picocolors12.default.green(` \u2713 Wrote ${edits.length} change${edits.length === 1 ? "" : "s"}. Review the diff, run your typecheck.`));
280994
+ console.log(import_picocolors13.default.green(` \u2713 Wrote ${edits.length} change${edits.length === 1 ? "" : "s"}. Review the diff, run your typecheck.`));
280808
280995
  } else {
280809
- console.log(import_picocolors12.default.dim(" Dry run. Re-run with `--write` to apply the auto-fixes above (manual items are never auto-written)."));
280996
+ console.log(import_picocolors13.default.dim(" Dry run. Re-run with `--write` to apply the auto-fixes above (manual items are never auto-written)."));
280810
280997
  }
280811
280998
  }
280812
280999
 
280813
281000
  // src/cli/pull.ts
280814
281001
  init_cjs_shims();
280815
- var import_picocolors13 = __toESM(require_picocolors(), 1);
280816
- var import_fs8 = require("fs");
281002
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
281003
+ var import_fs9 = require("fs");
280817
281004
  var DEFAULT_OUT2 = "ablo/schema.ts";
280818
281005
  var DEFAULT_IMPORT = "@abloatai/ablo/schema";
280819
281006
  var TENANCY_COLUMN = "organization_id";
@@ -280924,53 +281111,53 @@ async function pull(argv) {
280924
281111
  try {
280925
281112
  args = parsePullArgs(argv);
280926
281113
  } catch (err) {
280927
- console.error(import_picocolors13.default.red(` ${err instanceof Error ? err.message : String(err)}`));
281114
+ console.error(import_picocolors14.default.red(` ${err instanceof Error ? err.message : String(err)}`));
280928
281115
  process.exit(1);
280929
281116
  }
280930
281117
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
280931
281118
  if (!dbUrl) {
280932
- console.error(import_picocolors13.default.red(` No database.`) + import_picocolors13.default.dim(` Set ${import_picocolors13.default.bold("DATABASE_URL")} to the Postgres to pull from.`));
281119
+ console.error(import_picocolors14.default.red(` No database.`) + import_picocolors14.default.dim(` Set ${import_picocolors14.default.bold("DATABASE_URL")} to the Postgres to pull from.`));
280933
281120
  process.exit(1);
280934
281121
  }
280935
- if ((0, import_fs8.existsSync)(args.out) && !args.force) {
281122
+ if ((0, import_fs9.existsSync)(args.out) && !args.force) {
280936
281123
  console.error(
280937
- import_picocolors13.default.red(` ${args.out} already exists.`) + import_picocolors13.default.dim(` Re-run with ${import_picocolors13.default.bold("--force")} to overwrite.`)
281124
+ import_picocolors14.default.red(` ${args.out} already exists.`) + import_picocolors14.default.dim(` Re-run with ${import_picocolors14.default.bold("--force")} to overwrite.`)
280938
281125
  );
280939
281126
  process.exit(1);
280940
281127
  }
280941
281128
  console.log(`
280942
- ${brand("ablo")} ${import_picocolors13.default.dim("pull")} ${import_picocolors13.default.dim(`schema "${args.appSchema}"`)}
281129
+ ${brand("ablo")} ${import_picocolors14.default.dim("pull")} ${import_picocolors14.default.dim(`schema "${args.appSchema}"`)}
280943
281130
  `);
280944
281131
  let result;
280945
281132
  try {
280946
281133
  result = await buildSchemaSourceFromDb({ dbUrl, appSchema: args.appSchema, importPath: args.importPath });
280947
281134
  } catch (err) {
280948
- console.error(import_picocolors13.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
281135
+ console.error(import_picocolors14.default.red(` Couldn't read the database: ${err instanceof Error ? err.message : String(err)}`));
280949
281136
  process.exit(1);
280950
281137
  }
280951
281138
  if (result.models.length === 0) {
280952
281139
  console.error(
280953
- import_picocolors13.default.yellow(` No adoptable tables found`) + import_picocolors13.default.dim(` (a model needs an ${import_picocolors13.default.bold("id")} + ${import_picocolors13.default.bold("organization_id")} column).`)
281140
+ import_picocolors14.default.yellow(` No adoptable tables found`) + import_picocolors14.default.dim(` (a model needs an ${import_picocolors14.default.bold("id")} + ${import_picocolors14.default.bold("organization_id")} column).`)
280954
281141
  );
280955
281142
  process.exit(1);
280956
281143
  }
280957
- (0, import_fs8.writeFileSync)(args.out, result.source);
280958
- console.log(` ${import_picocolors13.default.green("\u2713")} wrote ${import_picocolors13.default.bold(args.out)} ${import_picocolors13.default.dim(`(${result.models.length} models)`)}`);
280959
- console.log(` ${import_picocolors13.default.dim(`models: ${result.models.join(", ")}`)}`);
281144
+ (0, import_fs9.writeFileSync)(args.out, result.source);
281145
+ console.log(` ${import_picocolors14.default.green("\u2713")} wrote ${import_picocolors14.default.bold(args.out)} ${import_picocolors14.default.dim(`(${result.models.length} models)`)}`);
281146
+ console.log(` ${import_picocolors14.default.dim(`models: ${result.models.join(", ")}`)}`);
280960
281147
  if (result.skipped > 0) {
280961
- console.log(` ${import_picocolors13.default.dim(`${result.skipped} table(s) skipped \u2014 no id/organization_id`)}`);
281148
+ console.log(` ${import_picocolors14.default.dim(`${result.skipped} table(s) skipped \u2014 no id/organization_id`)}`);
280962
281149
  }
280963
281150
  console.log(
280964
281151
  `
280965
- ${import_picocolors13.default.dim("Introspection is lossy (enums, JSON shape, relations). Review the file, then")} ${import_picocolors13.default.bold("ablo check")}.
281152
+ ${import_picocolors14.default.dim("Introspection is lossy (enums, JSON shape, relations). Review the file, then")} ${import_picocolors14.default.bold("ablo check")}.
280966
281153
  `
280967
281154
  );
280968
281155
  }
280969
281156
 
280970
281157
  // src/cli/prisma-pull.ts
280971
281158
  init_cjs_shims();
280972
- var import_picocolors14 = __toESM(require_picocolors(), 1);
280973
- var import_fs9 = require("fs");
281159
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
281160
+ var import_fs10 = require("fs");
280974
281161
 
280975
281162
  // src/cli/schema-ir.ts
280976
281163
  init_cjs_shims();
@@ -281247,57 +281434,57 @@ async function prismaPull(argv) {
281247
281434
  try {
281248
281435
  args = parsePrismaPullArgs(argv);
281249
281436
  } catch (err) {
281250
- console.error(import_picocolors14.default.red(` ${err instanceof Error ? err.message : String(err)}`));
281437
+ console.error(import_picocolors15.default.red(` ${err instanceof Error ? err.message : String(err)}`));
281251
281438
  process.exit(1);
281252
281439
  }
281253
- if (!(0, import_fs9.existsSync)(args.schema)) {
281440
+ if (!(0, import_fs10.existsSync)(args.schema)) {
281254
281441
  console.error(
281255
- import_picocolors14.default.red(` No Prisma schema at ${import_picocolors14.default.bold(args.schema)}.`) + import_picocolors14.default.dim(` Pass a path: ${import_picocolors14.default.bold("ablo pull prisma <path>")}.`)
281442
+ import_picocolors15.default.red(` No Prisma schema at ${import_picocolors15.default.bold(args.schema)}.`) + import_picocolors15.default.dim(` Pass a path: ${import_picocolors15.default.bold("ablo pull prisma <path>")}.`)
281256
281443
  );
281257
281444
  process.exit(1);
281258
281445
  }
281259
- if ((0, import_fs9.existsSync)(args.out) && !args.force) {
281446
+ if ((0, import_fs10.existsSync)(args.out) && !args.force) {
281260
281447
  console.error(
281261
- import_picocolors14.default.red(` ${args.out} already exists.`) + import_picocolors14.default.dim(` Re-run with ${import_picocolors14.default.bold("--force")} to overwrite.`)
281448
+ import_picocolors15.default.red(` ${args.out} already exists.`) + import_picocolors15.default.dim(` Re-run with ${import_picocolors15.default.bold("--force")} to overwrite.`)
281262
281449
  );
281263
281450
  process.exit(1);
281264
281451
  }
281265
281452
  console.log(`
281266
- ${brand("ablo")} ${import_picocolors14.default.dim("pull prisma")} ${import_picocolors14.default.dim(args.schema)}
281453
+ ${brand("ablo")} ${import_picocolors15.default.dim("pull prisma")} ${import_picocolors15.default.dim(args.schema)}
281267
281454
  `);
281268
281455
  let result;
281269
281456
  try {
281270
- const src = (0, import_fs9.readFileSync)(args.schema, "utf8");
281457
+ const src = (0, import_fs10.readFileSync)(args.schema, "utf8");
281271
281458
  result = buildSchemaSourceFromPrisma({ src, importPath: args.importPath });
281272
281459
  } catch (err) {
281273
- console.error(import_picocolors14.default.red(` Couldn't parse the schema: ${err instanceof Error ? err.message : String(err)}`));
281460
+ console.error(import_picocolors15.default.red(` Couldn't parse the schema: ${err instanceof Error ? err.message : String(err)}`));
281274
281461
  process.exit(1);
281275
281462
  }
281276
281463
  if (result.models.length === 0) {
281277
281464
  console.error(
281278
- import_picocolors14.default.yellow(` No adoptable models found`) + import_picocolors14.default.dim(` (a model needs an ${import_picocolors14.default.bold("id")} + ${import_picocolors14.default.bold("organizationId")} / ${import_picocolors14.default.bold("organization_id")}).`)
281465
+ import_picocolors15.default.yellow(` No adoptable models found`) + import_picocolors15.default.dim(` (a model needs an ${import_picocolors15.default.bold("id")} + ${import_picocolors15.default.bold("organizationId")} / ${import_picocolors15.default.bold("organization_id")}).`)
281279
281466
  );
281280
281467
  process.exit(1);
281281
281468
  }
281282
- (0, import_fs9.writeFileSync)(args.out, result.source);
281283
- console.log(` ${import_picocolors14.default.green("\u2713")} wrote ${import_picocolors14.default.bold(args.out)} ${import_picocolors14.default.dim(`(${result.models.length} models)`)}`);
281284
- console.log(` ${import_picocolors14.default.dim(`models: ${result.models.join(", ")}`)}`);
281469
+ (0, import_fs10.writeFileSync)(args.out, result.source);
281470
+ console.log(` ${import_picocolors15.default.green("\u2713")} wrote ${import_picocolors15.default.bold(args.out)} ${import_picocolors15.default.dim(`(${result.models.length} models)`)}`);
281471
+ console.log(` ${import_picocolors15.default.dim(`models: ${result.models.join(", ")}`)}`);
281285
281472
  if (result.skipped.length > 0) {
281286
- console.log(` ${import_picocolors14.default.dim(`${result.skipped.length} model(s) skipped:`)}`);
281287
- for (const s of result.skipped) console.log(` ${import_picocolors14.default.dim(`- ${s.name}: ${s.reason}`)}`);
281473
+ console.log(` ${import_picocolors15.default.dim(`${result.skipped.length} model(s) skipped:`)}`);
281474
+ for (const s of result.skipped) console.log(` ${import_picocolors15.default.dim(`- ${s.name}: ${s.reason}`)}`);
281288
281475
  }
281289
281476
  console.log(
281290
281477
  `
281291
- ${import_picocolors14.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors14.default.bold("ablo check")}.
281478
+ ${import_picocolors15.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors15.default.bold("ablo check")}.
281292
281479
  `
281293
281480
  );
281294
281481
  }
281295
281482
 
281296
281483
  // src/cli/drizzle-pull.ts
281297
281484
  init_cjs_shims();
281298
- var import_picocolors15 = __toESM(require_picocolors(), 1);
281299
- var import_fs10 = require("fs");
281300
- var import_path5 = require("path");
281485
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
281486
+ var import_fs11 = require("fs");
281487
+ var import_path6 = require("path");
281301
281488
  var DEFAULT_OUT4 = "ablo/schema.ts";
281302
281489
  var DEFAULT_IMPORT3 = "@abloatai/ablo/schema";
281303
281490
  var BASE_FIELD_NAMES2 = /* @__PURE__ */ new Set(["id", "organizationId", "createdBy", "createdAt", "updatedAt"]);
@@ -281423,7 +281610,7 @@ function parseDrizzlePullArgs(argv) {
281423
281610
  async function loadModule(path) {
281424
281611
  const { createJiti } = await import("jiti");
281425
281612
  const jiti = createJiti(process.cwd());
281426
- const mod = await jiti.import((0, import_path5.resolve)(path));
281613
+ const mod = await jiti.import((0, import_path6.resolve)(path));
281427
281614
  return mod;
281428
281615
  }
281429
281616
  async function drizzlePull(argv) {
@@ -281431,27 +281618,27 @@ async function drizzlePull(argv) {
281431
281618
  try {
281432
281619
  args = parseDrizzlePullArgs(argv);
281433
281620
  } catch (err) {
281434
- console.error(import_picocolors15.default.red(` ${err instanceof Error ? err.message : String(err)}`));
281621
+ console.error(import_picocolors16.default.red(` ${err instanceof Error ? err.message : String(err)}`));
281435
281622
  process.exit(1);
281436
281623
  }
281437
281624
  if (!args.schema) {
281438
281625
  console.error(
281439
- import_picocolors15.default.red(` No Drizzle schema given.`) + import_picocolors15.default.dim(` Pass the module: ${import_picocolors15.default.bold("ablo pull drizzle src/db/schema.ts")}.`)
281626
+ import_picocolors16.default.red(` No Drizzle schema given.`) + import_picocolors16.default.dim(` Pass the module: ${import_picocolors16.default.bold("ablo pull drizzle src/db/schema.ts")}.`)
281440
281627
  );
281441
281628
  process.exit(1);
281442
281629
  }
281443
- if (!(0, import_fs10.existsSync)(args.schema)) {
281444
- console.error(import_picocolors15.default.red(` No file at ${import_picocolors15.default.bold(args.schema)}.`));
281630
+ if (!(0, import_fs11.existsSync)(args.schema)) {
281631
+ console.error(import_picocolors16.default.red(` No file at ${import_picocolors16.default.bold(args.schema)}.`));
281445
281632
  process.exit(1);
281446
281633
  }
281447
- if ((0, import_fs10.existsSync)(args.out) && !args.force) {
281634
+ if ((0, import_fs11.existsSync)(args.out) && !args.force) {
281448
281635
  console.error(
281449
- import_picocolors15.default.red(` ${args.out} already exists.`) + import_picocolors15.default.dim(` Re-run with ${import_picocolors15.default.bold("--force")} to overwrite.`)
281636
+ import_picocolors16.default.red(` ${args.out} already exists.`) + import_picocolors16.default.dim(` Re-run with ${import_picocolors16.default.bold("--force")} to overwrite.`)
281450
281637
  );
281451
281638
  process.exit(1);
281452
281639
  }
281453
281640
  console.log(`
281454
- ${brand("ablo")} ${import_picocolors15.default.dim("pull drizzle")} ${import_picocolors15.default.dim(args.schema)}
281641
+ ${brand("ablo")} ${import_picocolors16.default.dim("pull drizzle")} ${import_picocolors16.default.dim(args.schema)}
281455
281642
  `);
281456
281643
  let result;
281457
281644
  try {
@@ -281459,33 +281646,33 @@ async function drizzlePull(argv) {
281459
281646
  result = await buildSchemaSourceFromDrizzle({ mod, importPath: args.importPath });
281460
281647
  } catch (err) {
281461
281648
  const msg = err instanceof Error ? err.message : String(err);
281462
- const hint = /Cannot find package 'drizzle-orm'/.test(msg) ? import_picocolors15.default.dim(` (install ${import_picocolors15.default.bold("drizzle-orm")} in this project)`) : "";
281463
- console.error(import_picocolors15.default.red(` Couldn't load the schema: ${msg}`) + hint);
281649
+ const hint = /Cannot find package 'drizzle-orm'/.test(msg) ? import_picocolors16.default.dim(` (install ${import_picocolors16.default.bold("drizzle-orm")} in this project)`) : "";
281650
+ console.error(import_picocolors16.default.red(` Couldn't load the schema: ${msg}`) + hint);
281464
281651
  process.exit(1);
281465
281652
  }
281466
281653
  if (result.models.length === 0) {
281467
281654
  console.error(
281468
- import_picocolors15.default.yellow(` No adoptable tables found`) + import_picocolors15.default.dim(` (a table needs an ${import_picocolors15.default.bold("id")} + ${import_picocolors15.default.bold("organization_id")} column).`)
281655
+ import_picocolors16.default.yellow(` No adoptable tables found`) + import_picocolors16.default.dim(` (a table needs an ${import_picocolors16.default.bold("id")} + ${import_picocolors16.default.bold("organization_id")} column).`)
281469
281656
  );
281470
281657
  process.exit(1);
281471
281658
  }
281472
- (0, import_fs10.writeFileSync)(args.out, result.source);
281473
- console.log(` ${import_picocolors15.default.green("\u2713")} wrote ${import_picocolors15.default.bold(args.out)} ${import_picocolors15.default.dim(`(${result.models.length} models)`)}`);
281474
- console.log(` ${import_picocolors15.default.dim(`models: ${result.models.join(", ")}`)}`);
281659
+ (0, import_fs11.writeFileSync)(args.out, result.source);
281660
+ console.log(` ${import_picocolors16.default.green("\u2713")} wrote ${import_picocolors16.default.bold(args.out)} ${import_picocolors16.default.dim(`(${result.models.length} models)`)}`);
281661
+ console.log(` ${import_picocolors16.default.dim(`models: ${result.models.join(", ")}`)}`);
281475
281662
  if (result.skipped.length > 0) {
281476
- console.log(` ${import_picocolors15.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
281477
- for (const s of result.skipped) console.log(` ${import_picocolors15.default.dim(`- ${s.name}: ${s.reason}`)}`);
281663
+ console.log(` ${import_picocolors16.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
281664
+ for (const s of result.skipped) console.log(` ${import_picocolors16.default.dim(`- ${s.name}: ${s.reason}`)}`);
281478
281665
  }
281479
281666
  console.log(
281480
281667
  `
281481
- ${import_picocolors15.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors15.default.bold("ablo check")}.
281668
+ ${import_picocolors16.default.dim("Enums and relations were preserved. Review the file, then")} ${import_picocolors16.default.bold("ablo check")}.
281482
281669
  `
281483
281670
  );
281484
281671
  }
281485
281672
 
281486
281673
  // src/cli/index.ts
281487
281674
  var LOGO = `
281488
- ${brand("ablo")} ${import_picocolors16.default.dim("sync engine")}
281675
+ ${brand("ablo")} ${import_picocolors17.default.dim("sync engine")}
281489
281676
  `;
281490
281677
  async function main() {
281491
281678
  const command = process.argv[2];
@@ -281504,7 +281691,8 @@ async function main() {
281504
281691
  } else if (command === "webhooks") {
281505
281692
  await webhooks(process.argv.slice(3));
281506
281693
  } else if (command === "dev") {
281507
- await dev(process.argv.slice(3));
281694
+ console.log(import_picocolors17.default.dim(" `ablo dev` is now `ablo push --watch` \u2014 running that."));
281695
+ await dev([...process.argv.slice(3), "--watch"]);
281508
281696
  } else if (command === "check") {
281509
281697
  await check(process.argv.slice(3));
281510
281698
  } else if (command === "pull") {
@@ -281519,20 +281707,27 @@ async function main() {
281519
281707
  } else if (command === "migrate") {
281520
281708
  await migrate(process.argv.slice(3));
281521
281709
  } else if (command === "push") {
281522
- await push(process.argv.slice(3));
281710
+ const rest = process.argv.slice(3);
281711
+ const advanced = rest.some((a) => ["--force", "--rename", "--backfill", "--url"].includes(a));
281712
+ const liveKey = (process.env.ABLO_API_KEY ?? "").startsWith("sk_live_");
281713
+ if (advanced || liveKey) {
281714
+ await push(rest);
281715
+ } else {
281716
+ await dev(rest);
281717
+ }
281523
281718
  } else if (command === "upgrade") {
281524
281719
  await upgrade(process.argv.slice(3));
281525
281720
  } else if (command === "generate") {
281526
281721
  await generate(process.argv.slice(3));
281527
281722
  } else if (command === "schema") {
281528
281723
  console.error(
281529
- ` ${import_picocolors16.default.red("\u2717")} \`ablo schema push\` was renamed to \`${brand("ablo push")}\`.`
281724
+ ` ${import_picocolors17.default.red("\u2717")} \`ablo schema push\` was renamed to \`${brand("ablo push")}\`.`
281530
281725
  );
281531
281726
  console.error(` Run \`ablo push${process.argv.slice(4).join(" ") ? " " + process.argv.slice(4).join(" ") : ""}\` instead.`);
281532
281727
  process.exitCode = 1;
281533
281728
  } else {
281534
281729
  console.log(LOGO);
281535
- console.log(` ${import_picocolors16.default.bold("Usage:")}`);
281730
+ console.log(` ${import_picocolors17.default.bold("Usage:")}`);
281536
281731
  console.log(` npx ablo init Scaffold ablo/ directory + starter schema`);
281537
281732
  console.log(` npx ablo init --yes [--framework nextjs] Non-interactive (agents/CI): no prompts, flag-driven`);
281538
281733
  console.log(` [--auth apikey] [--storage direct|endpoint] [--no-agent] [--no-pull] [--no-install] [--no-login]`);
@@ -281557,10 +281752,10 @@ async function main() {
281557
281752
  console.log(` npx ablo generate Emit TypeScript types from your schema`);
281558
281753
  console.log(` npx ablo generate --out path.ts Write generated types to a path`);
281559
281754
  console.log();
281560
- console.log(` ${import_picocolors16.default.bold("Schema workflow:")}`);
281755
+ console.log(` ${import_picocolors17.default.bold("Schema workflow:")}`);
281561
281756
  console.log(` The server holds its own copy of your schema \u2014 edit ${brand("ablo/schema.ts")}, then`);
281562
281757
  console.log(` run ${brand("ablo push")} (or keep ${brand("ablo dev")} running) before the server will accept`);
281563
- console.log(` writes to new or changed models. Skip it and writes fail with ${import_picocolors16.default.yellow("server_execute_unknown_model")}.`);
281758
+ console.log(` writes to new or changed models. Skip it and writes fail with ${import_picocolors17.default.yellow("server_execute_unknown_model")}.`);
281564
281759
  console.log();
281565
281760
  }
281566
281761
  }
@@ -281597,7 +281792,7 @@ function parseInitArgs(args) {
281597
281792
  function detectOrm(override) {
281598
281793
  if (override === "prisma" || override === "drizzle" || override === "none") return override;
281599
281794
  try {
281600
- const pkg = JSON.parse((0, import_fs11.readFileSync)("package.json", "utf-8"));
281795
+ const pkg = JSON.parse((0, import_fs12.readFileSync)("package.json", "utf-8"));
281601
281796
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
281602
281797
  if (deps["@prisma/client"] || deps["prisma"]) return "prisma";
281603
281798
  if (deps["drizzle-orm"]) return "drizzle";
@@ -281628,8 +281823,8 @@ async function chooseBool(flagValue, fallback, interactive, prompt) {
281628
281823
  async function init(args = []) {
281629
281824
  const opts = parseInitArgs(args);
281630
281825
  const interactive = Boolean(process.stdin.isTTY) && !opts.yes && !process.env.CI;
281631
- Ie(`${brand("ablo")} ${import_picocolors16.default.dim("sync engine")}`);
281632
- if (!(0, import_fs11.existsSync)("package.json")) {
281826
+ Ie(`${brand("ablo")} ${import_picocolors17.default.dim("sync engine")}`);
281827
+ if (!(0, import_fs12.existsSync)("package.json")) {
281633
281828
  xe("No package.json found. Run this from your project root.");
281634
281829
  process.exit(1);
281635
281830
  }
@@ -281705,14 +281900,14 @@ async function init(args = []) {
281705
281900
  );
281706
281901
  }
281707
281902
  const abloDir = "ablo";
281708
- (0, import_fs11.mkdirSync)(abloDir, { recursive: true });
281903
+ (0, import_fs12.mkdirSync)(abloDir, { recursive: true });
281709
281904
  const created = [];
281710
281905
  let schemaSource = generateSchema();
281711
281906
  let schemaNote = "";
281712
281907
  if (pullExisting) {
281713
281908
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
281714
281909
  if (!dbUrl) {
281715
- schemaNote = import_picocolors16.default.dim(" (no DATABASE_URL \u2014 wrote starter; run `ablo pull` later)");
281910
+ schemaNote = import_picocolors17.default.dim(" (no DATABASE_URL \u2014 wrote starter; run `ablo pull` later)");
281716
281911
  } else {
281717
281912
  try {
281718
281913
  const pulled = await buildSchemaSourceFromDb({
@@ -281722,60 +281917,60 @@ async function init(args = []) {
281722
281917
  });
281723
281918
  if (pulled.models.length > 0) {
281724
281919
  schemaSource = pulled.source;
281725
- schemaNote = import_picocolors16.default.dim(` (pulled ${pulled.models.length} models)`);
281920
+ schemaNote = import_picocolors17.default.dim(` (pulled ${pulled.models.length} models)`);
281726
281921
  } else {
281727
- schemaNote = import_picocolors16.default.dim(" (no adoptable tables \u2014 wrote starter)");
281922
+ schemaNote = import_picocolors17.default.dim(" (no adoptable tables \u2014 wrote starter)");
281728
281923
  }
281729
281924
  } catch {
281730
- schemaNote = import_picocolors16.default.dim(" (pull failed \u2014 wrote starter)");
281925
+ schemaNote = import_picocolors17.default.dim(" (pull failed \u2014 wrote starter)");
281731
281926
  }
281732
281927
  }
281733
281928
  }
281734
- (0, import_fs11.writeFileSync)((0, import_path6.join)(abloDir, "schema.ts"), schemaSource);
281929
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "schema.ts"), schemaSource);
281735
281930
  created.push(`${abloDir}/schema.ts${schemaNote}`);
281736
- (0, import_fs11.writeFileSync)((0, import_path6.join)(abloDir, "index.ts"), generateSyncConfig(auth, storage));
281931
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "index.ts"), generateSyncConfig(auth, storage));
281737
281932
  created.push(`${abloDir}/index.ts`);
281738
281933
  const orm = detectOrm(opts.orm);
281739
281934
  if (storage === "endpoint") {
281740
- (0, import_fs11.writeFileSync)((0, import_path6.join)(abloDir, "data-source.ts"), generateDataSource(orm));
281935
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "data-source.ts"), generateDataSource(orm));
281741
281936
  created.push(`${abloDir}/data-source.ts${orm === "drizzle" ? " (Drizzle)" : " (Prisma)"}`);
281742
281937
  }
281743
281938
  const envFile = framework === "nextjs" ? ".env.local" : ".env";
281744
- if (!(0, import_fs11.existsSync)(envFile)) {
281745
- (0, import_fs11.writeFileSync)(envFile, generateEnv(storage));
281939
+ if (!(0, import_fs12.existsSync)(envFile)) {
281940
+ (0, import_fs12.writeFileSync)(envFile, generateEnv(storage));
281746
281941
  created.push(envFile);
281747
281942
  } else {
281748
- const existing = (0, import_fs11.readFileSync)(envFile, "utf-8");
281943
+ const existing = (0, import_fs12.readFileSync)(envFile, "utf-8");
281749
281944
  if (!existing.includes("ABLO_")) {
281750
- (0, import_fs11.writeFileSync)(envFile, existing + "\n" + generateEnv(storage));
281751
- created.push(`${envFile} ${import_picocolors16.default.dim("(appended)")}`);
281945
+ (0, import_fs12.writeFileSync)(envFile, existing + "\n" + generateEnv(storage));
281946
+ created.push(`${envFile} ${import_picocolors17.default.dim("(appended)")}`);
281752
281947
  } else {
281753
- created.push(`${envFile} ${import_picocolors16.default.dim("(already configured)")}`);
281948
+ created.push(`${envFile} ${import_picocolors17.default.dim("(already configured)")}`);
281754
281949
  }
281755
281950
  }
281756
281951
  if (agent) {
281757
- (0, import_fs11.writeFileSync)((0, import_path6.join)(abloDir, "agent.ts"), generateAgent());
281952
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "agent.ts"), generateAgent());
281758
281953
  created.push(`${abloDir}/agent.ts`);
281759
281954
  }
281760
281955
  if (framework === "nextjs") {
281761
281956
  if (storage === "endpoint") {
281762
- const webhookDir = (0, import_path6.join)("app", "api", "ablo", "webhooks");
281763
- (0, import_fs11.mkdirSync)(webhookDir, { recursive: true });
281764
- (0, import_fs11.writeFileSync)((0, import_path6.join)(webhookDir, "route.ts"), generateWebhookRoute(orm));
281957
+ const webhookDir = (0, import_path7.join)("app", "api", "ablo", "webhooks");
281958
+ (0, import_fs12.mkdirSync)(webhookDir, { recursive: true });
281959
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(webhookDir, "route.ts"), generateWebhookRoute(orm));
281765
281960
  created.push(`${webhookDir}/route.ts${orm === "prisma" ? " (Prisma mirror)" : " (add your database write)"}`);
281766
281961
  }
281767
- (0, import_fs11.writeFileSync)((0, import_path6.join)("app", "providers.tsx"), generateProviders());
281768
- created.push(`app/providers.tsx ${import_picocolors16.default.dim("(wrap app/layout.tsx in <Providers>)")}`);
281769
- const sessionDir = (0, import_path6.join)("app", "api", "ablo-session");
281770
- (0, import_fs11.mkdirSync)(sessionDir, { recursive: true });
281771
- (0, import_fs11.writeFileSync)((0, import_path6.join)(sessionDir, "route.ts"), generateSessionRoute());
281772
- created.push(`app/api/ablo-session/route.ts ${import_picocolors16.default.dim("(wire your auth)")}`);
281962
+ (0, import_fs12.writeFileSync)((0, import_path7.join)("app", "providers.tsx"), generateProviders());
281963
+ created.push(`app/providers.tsx ${import_picocolors17.default.dim("(wrap app/layout.tsx in <Providers>)")}`);
281964
+ const sessionDir = (0, import_path7.join)("app", "api", "ablo-session");
281965
+ (0, import_fs12.mkdirSync)(sessionDir, { recursive: true });
281966
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(sessionDir, "route.ts"), generateSessionRoute());
281967
+ created.push(`app/api/ablo-session/route.ts ${import_picocolors17.default.dim("(wire your auth)")}`);
281773
281968
  }
281774
281969
  if (framework !== "vanilla") {
281775
- (0, import_fs11.writeFileSync)((0, import_path6.join)(abloDir, "TaskList.tsx"), generateComponent());
281970
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "TaskList.tsx"), generateComponent());
281776
281971
  created.push(`${abloDir}/TaskList.tsx`);
281777
281972
  }
281778
- Me(created.map((f) => `${import_picocolors16.default.green("\u2713")} ${f}`).join("\n"), "Created");
281973
+ Me(created.map((f) => `${import_picocolors17.default.green("\u2713")} ${f}`).join("\n"), "Created");
281779
281974
  const pm = detectPackageManager();
281780
281975
  if (opts.install) {
281781
281976
  const s = Y2();
@@ -281784,38 +281979,43 @@ async function init(args = []) {
281784
281979
  (0, import_child_process2.execSync)(`${pm} add @abloatai/ablo`, { stdio: "ignore" });
281785
281980
  s.stop("Installed @abloatai/ablo");
281786
281981
  } catch {
281787
- s.stop(`${import_picocolors16.default.yellow("!")} Couldn't auto-install \u2014 run ${import_picocolors16.default.bold(`${pm} install @abloatai/ablo`)}`);
281982
+ s.stop(`${import_picocolors17.default.yellow("!")} Couldn't auto-install \u2014 run ${import_picocolors17.default.bold(`${pm} install @abloatai/ablo`)}`);
281788
281983
  }
281789
281984
  }
281790
281985
  const steps = [
281791
- `Get a ${import_picocolors16.default.bold("sk_test_")} key at ${import_picocolors16.default.cyan("https://abloatai.com")}`,
281792
- `Run ${import_picocolors16.default.bold("npx ablo login")} (or add ${import_picocolors16.default.bold("ABLO_API_KEY")} to ${import_picocolors16.default.bold(envFile)})`,
281793
- `Set ${import_picocolors16.default.bold("DATABASE_URL")} in ${import_picocolors16.default.bold(envFile)} \u2014 your Postgres is the system of record; rows live there, never with Ablo`,
281794
- `Run ${import_picocolors16.default.bold("npx ablo dev")} \u2014 pushes your schema definition and watches for changes`,
281986
+ `Get a ${import_picocolors17.default.bold("sk_test_")} key at ${import_picocolors17.default.cyan("https://abloatai.com")}`,
281987
+ `Run ${import_picocolors17.default.bold("npx ablo login")} (or add ${import_picocolors17.default.bold("ABLO_API_KEY")} to ${import_picocolors17.default.bold(envFile)})`,
281988
+ `Set ${import_picocolors17.default.bold("DATABASE_URL")} in ${import_picocolors17.default.bold(envFile)} \u2014 your Postgres is the system of record; rows live there, never with Ablo`,
281989
+ `Run ${import_picocolors17.default.bold("npx ablo dev")} \u2014 pushes your schema definition and watches for changes`,
281795
281990
  ...storage === "direct" ? [
281796
- `Provision your DB: ${import_picocolors16.default.bold("npx ablo migrate")} (creates your synced-model tables with row-level security; keep your own migrations for everything else)`
281991
+ `Provision your DB: ${import_picocolors17.default.bold("npx ablo migrate")} (creates your synced-model tables with row-level security; keep your own migrations for everything else)`
281797
281992
  ] : [
281798
- `Provision your DB: ${import_picocolors16.default.bold("npx ablo migrate")} (creates your Ablo-model tables + the adapter tables; keep your own migrations for everything else), then mount ${import_picocolors16.default.bold(`${abloDir}/data-source.ts`)} at ${import_picocolors16.default.bold("/api/ablo/source")}`
281993
+ `Provision your DB: ${import_picocolors17.default.bold("npx ablo migrate")} (creates your Ablo-model tables + the adapter tables; keep your own migrations for everything else), then mount ${import_picocolors17.default.bold(`${abloDir}/data-source.ts`)} at ${import_picocolors17.default.bold("/api/ablo/source")}`
281799
281994
  ],
281800
281995
  ...framework === "nextjs" ? [
281801
- `Wrap ${import_picocolors16.default.bold("app/layout.tsx")} in ${import_picocolors16.default.bold("<Providers>")} (app/providers.tsx) and add your auth to ${import_picocolors16.default.bold("app/api/ablo-session/route.ts")}`
281996
+ `Wrap ${import_picocolors17.default.bold("app/layout.tsx")} in ${import_picocolors17.default.bold("<Providers>")} (app/providers.tsx) and add your auth to ${import_picocolors17.default.bold("app/api/ablo-session/route.ts")}`
281802
281997
  ] : [],
281803
- `Run ${import_picocolors16.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
281998
+ `Run ${import_picocolors17.default.bold(`${pm} run dev`)} and open two browser tabs \u2014 changes sync in real-time`,
281804
281999
  ...agent ? [
281805
- `Run ${import_picocolors16.default.bold(`npx tsx ${abloDir}/agent.ts`)} \u2014 an AI teammate edits the same tasks`,
281806
- `Run ${import_picocolors16.default.bold("npx ablo logs")} to watch human + agent commits stream by`
282000
+ `Run ${import_picocolors17.default.bold(`npx tsx ${abloDir}/agent.ts`)} \u2014 an AI teammate edits the same tasks`,
282001
+ `Run ${import_picocolors17.default.bold("npx ablo logs")} to watch human + agent commits stream by`
281807
282002
  ] : []
281808
282003
  ];
281809
282004
  Me(steps.map((s, i) => `${i + 1}. ${s}`).join("\n"), "Next steps");
282005
+ const existingKey = resolveApiKey("sandbox");
282006
+ if (existingKey) {
282007
+ Se(`Already authorized ${import_picocolors17.default.dim(`(${existingKey.slice(0, 11)}\u2026)`)} \u2014 run ${import_picocolors17.default.bold("npx ablo push")} next. ${import_picocolors17.default.dim("Docs:")} https://abloatai.com/docs`);
282008
+ return;
282009
+ }
281810
282010
  if (interactive && opts.login) {
281811
282011
  const loginNow = await ye({ message: "Log in now? (opens your browser)", initialValue: true });
281812
282012
  if (!pD(loginNow) && loginNow) {
281813
- Se(`${import_picocolors16.default.dim("Docs:")} https://abloatai.com/docs`);
282013
+ Se(`${import_picocolors17.default.dim("Docs:")} https://abloatai.com/docs`);
281814
282014
  await login();
281815
282015
  return;
281816
282016
  }
281817
282017
  }
281818
- Se(`Run ${import_picocolors16.default.bold("npx ablo login")} when ready. ${import_picocolors16.default.dim("Docs:")} https://abloatai.com/docs`);
282018
+ Se(`Run ${import_picocolors17.default.bold("npx ablo login")} when ready. ${import_picocolors17.default.dim("Docs:")} https://abloatai.com/docs`);
281819
282019
  }
281820
282020
  function generateSchema() {
281821
282021
  return `import { defineSchema, model, relation, z } from '@abloatai/ablo/schema';
@@ -282169,9 +282369,9 @@ async function getCurrentUser(): Promise<{ id: string } | null> {
282169
282369
  `;
282170
282370
  }
282171
282371
  function detectPackageManager() {
282172
- if ((0, import_fs11.existsSync)("pnpm-lock.yaml")) return "pnpm";
282173
- if ((0, import_fs11.existsSync)("yarn.lock")) return "yarn";
282174
- if ((0, import_fs11.existsSync)("bun.lockb")) return "bun";
282372
+ if ((0, import_fs12.existsSync)("pnpm-lock.yaml")) return "pnpm";
282373
+ if ((0, import_fs12.existsSync)("yarn.lock")) return "yarn";
282374
+ if ((0, import_fs12.existsSync)("bun.lockb")) return "bun";
282175
282375
  return "npm";
282176
282376
  }
282177
282377
  main().catch(console.error);