@abloatai/ablo 0.9.5 → 0.9.7

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
@@ -276700,8 +276700,8 @@ 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");
276703
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
276704
+ var import_fs12 = require("fs");
276705
276705
  var import_path7 = require("path");
276706
276706
  var import_child_process2 = require("child_process");
276707
276707
 
@@ -276787,13 +276787,22 @@ var ERROR_CODES = {
276787
276787
  byo_tenant_tables_unforced_rls: wire("permission", 403, false, "Tenant tables do not have RLS forced under the direct Postgres connector role."),
276788
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."),
276789
276789
  // ── claim / intent conflict (409) ──────────────────────────────────
276790
- claim_conflict: wire("claim", 409, true, "The target entity is claimed by another participant."),
276791
- claim_lost: wire("claim", 409, true, "A previously held claim was lost before the write applied."),
276792
- entity_claimed: wire("claim", 409, true, "The target entity is currently claimed; write was blocked."),
276793
- 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)."),
276794
276803
  malformed_claim: wire("claim", 400, false, "The claim payload was malformed."),
276795
- model_claimed: wire("claim", 409, true, "The model instance is claimed by another participant."),
276796
- 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."),
276797
276806
  model_claim_not_configured: client("claim", "Claiming was requested on a model that has no claim configuration."),
276798
276807
  // ── stale context / idempotency (409) ──────────────────────────────
276799
276808
  stale_context: wire("conflict", 409, true, "The write carried a readAt watermark that is now stale; re-read and retry."),
@@ -277088,9 +277097,8 @@ var ErrorBodyShapeSchema = import_zod2.z.object({
277088
277097
  }).passthrough();
277089
277098
 
277090
277099
  // src/cli/migrate.ts
277091
- var import_picocolors3 = __toESM(require_picocolors(), 1);
277092
- var import_fs4 = require("fs");
277093
- var import_path3 = require("path");
277100
+ var import_picocolors4 = __toESM(require_picocolors(), 1);
277101
+ var import_fs5 = require("fs");
277094
277102
 
277095
277103
  // node_modules/postgres/src/index.js
277096
277104
  init_cjs_shims();
@@ -279229,6 +279237,9 @@ function osUsername() {
279229
279237
  // src/cli/dbRole.ts
279230
279238
  init_cjs_shims();
279231
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");
279232
279243
  var DEFAULT_SCOPED_ROLE = "ablo_app";
279233
279244
  async function detectRoleSafety(sql) {
279234
279245
  const rows = await sql`SELECT rolname, rolsuper, rolbypassrls FROM pg_roles WHERE rolname = current_user`;
@@ -279300,6 +279311,94 @@ async function createScopedRole(ownerUrl, options) {
279300
279311
  }
279301
279312
  return { role, databaseUrl: rewriteDatabaseUrl(ownerUrl, role, password) };
279302
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
+ }
279303
279402
 
279304
279403
  // src/cli/migrate.ts
279305
279404
  var import_schema2 = require("@abloatai/ablo/schema");
@@ -279307,26 +279406,26 @@ var import_source = require("@abloatai/ablo/source");
279307
279406
 
279308
279407
  // src/cli/push.ts
279309
279408
  init_cjs_shims();
279310
- var import_picocolors2 = __toESM(require_picocolors(), 1);
279311
- var import_fs3 = require("fs");
279312
- 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");
279313
279412
  var import_schema = require("@abloatai/ablo/schema");
279314
279413
 
279315
279414
  // src/cli/config.ts
279316
279415
  init_cjs_shims();
279317
279416
  var import_os2 = require("os");
279318
- var import_path = require("path");
279319
- var import_fs2 = require("fs");
279417
+ var import_path2 = require("path");
279418
+ var import_fs3 = require("fs");
279320
279419
  function configDir() {
279321
279420
  if (process.env.ABLO_CONFIG_DIR) return process.env.ABLO_CONFIG_DIR;
279322
279421
  const xdg = process.env.XDG_CONFIG_HOME;
279323
- 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");
279324
279423
  }
279325
279424
  function configPath() {
279326
- return (0, import_path.join)(configDir(), "config.json");
279425
+ return (0, import_path2.join)(configDir(), "config.json");
279327
279426
  }
279328
279427
  function credentialsPath() {
279329
- return (0, import_path.join)(configDir(), "credentials.json");
279428
+ return (0, import_path2.join)(configDir(), "credentials.json");
279330
279429
  }
279331
279430
  function asKeyEntry(value) {
279332
279431
  if (value && typeof value === "object" && typeof value.apiKey === "string") {
@@ -279335,9 +279434,9 @@ function asKeyEntry(value) {
279335
279434
  return void 0;
279336
279435
  }
279337
279436
  function readJson(path) {
279338
- if (!(0, import_fs2.existsSync)(path)) return null;
279437
+ if (!(0, import_fs3.existsSync)(path)) return null;
279339
279438
  try {
279340
- const parsed = JSON.parse((0, import_fs2.readFileSync)(path, "utf-8"));
279439
+ const parsed = JSON.parse((0, import_fs3.readFileSync)(path, "utf-8"));
279341
279440
  return parsed && typeof parsed === "object" ? parsed : null;
279342
279441
  } catch {
279343
279442
  return null;
@@ -279374,14 +279473,14 @@ function readConfig() {
279374
279473
  }
279375
279474
  function writeConfig(cfg) {
279376
279475
  const dir = configDir();
279377
- (0, import_fs2.mkdirSync)(dir, { recursive: true, mode: 448 });
279378
- (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)}
279379
279478
  `, { mode: 384 });
279380
279479
  const credentials = {
279381
279480
  ...cfg.sandbox ? { sandbox: cfg.sandbox } : {},
279382
279481
  ...cfg.production ? { production: cfg.production } : {}
279383
279482
  };
279384
- (0, import_fs2.writeFileSync)(credentialsPath(), `${JSON.stringify(credentials, null, 2)}
279483
+ (0, import_fs3.writeFileSync)(credentialsPath(), `${JSON.stringify(credentials, null, 2)}
279385
279484
  `, { mode: 384 });
279386
279485
  return credentialsPath();
279387
279486
  }
@@ -279402,8 +279501,8 @@ function normalizeMode(value) {
279402
279501
  function clearCredential() {
279403
279502
  let removed = false;
279404
279503
  for (const path of [configPath(), credentialsPath()]) {
279405
- if ((0, import_fs2.existsSync)(path)) {
279406
- (0, import_fs2.rmSync)(path);
279504
+ if ((0, import_fs3.existsSync)(path)) {
279505
+ (0, import_fs3.rmSync)(path);
279407
279506
  removed = true;
279408
279507
  }
279409
279508
  }
@@ -279432,7 +279531,7 @@ var DEFAULT_URL = "https://api.abloatai.com";
279432
279531
  function fmtSignal(s) {
279433
279532
  const sig = s;
279434
279533
  const where = sig.field ? `${sig.model}.${sig.field}` : sig.model;
279435
- return ` \u2022 ${import_picocolors2.default.bold(where ?? "?")} \u2014 ${sig.detail ?? ""}`;
279534
+ return ` \u2022 ${import_picocolors3.default.bold(where ?? "?")} \u2014 ${sig.detail ?? ""}`;
279436
279535
  }
279437
279536
  async function pushSchema(schema, args) {
279438
279537
  const schemaJson = JSON.parse((0, import_schema.serializeSchema)(schema));
@@ -279510,10 +279609,10 @@ function parsePushArgs(argv) {
279510
279609
  return { schemaPath, exportName, url, apiKey: process.env.ABLO_API_KEY, force, renames, backfills };
279511
279610
  }
279512
279611
  async function loadSchema(schemaPath, exportName) {
279513
- const abs = (0, import_path2.resolve)(process.cwd(), schemaPath);
279514
- if (!(0, import_fs3.existsSync)(abs)) {
279612
+ const abs = (0, import_path3.resolve)(process.cwd(), schemaPath);
279613
+ if (!(0, import_fs4.existsSync)(abs)) {
279515
279614
  throw new AbloValidationError(
279516
- `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>")}.`,
279517
279616
  { code: "cli_invalid_arguments" }
279518
279617
  );
279519
279618
  }
@@ -279524,7 +279623,7 @@ async function loadSchema(schemaPath, exportName) {
279524
279623
  const schema = mod[exportName] ?? nested?.[exportName];
279525
279624
  if (!schema || typeof schema !== "object" || !("models" in schema)) {
279526
279625
  throw new AbloValidationError(
279527
- `${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({ ... })\`?`,
279528
279627
  { code: "cli_invalid_arguments" }
279529
279628
  );
279530
279629
  }
@@ -279535,30 +279634,30 @@ async function push(argv) {
279535
279634
  try {
279536
279635
  args = parsePushArgs(argv);
279537
279636
  } catch (err) {
279538
- 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)}`));
279539
279638
  process.exit(1);
279540
279639
  }
279541
279640
  if (!args.apiKey) args.apiKey = resolveApiKey();
279542
279641
  if (!args.apiKey) {
279543
279642
  console.error(
279544
- 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).`)
279545
279644
  );
279546
279645
  process.exit(1);
279547
279646
  }
279548
279647
  const schema = await loadSchema(args.schemaPath, args.exportName);
279549
279648
  const hash = (0, import_schema.schemaHash)(schema);
279550
279649
  console.log(
279551
- ` 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)}`
279552
279651
  );
279553
279652
  const { ok: resOk, status: status2, body, bodyText } = await pushSchema(schema, args);
279554
279653
  if (resOk) {
279555
279654
  if (body.unchanged) {
279556
- 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}).`);
279557
279656
  } else {
279558
- 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})`)}`);
279559
279658
  if (Array.isArray(body.warnings) && body.warnings.length > 0) {
279560
- console.log(import_picocolors2.default.yellow(` Applied ${body.warnings.length} destructive change(s):`));
279561
- 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)));
279562
279661
  }
279563
279662
  }
279564
279663
  return;
@@ -279566,20 +279665,20 @@ async function push(argv) {
279566
279665
  if (status2 === 409) {
279567
279666
  const unexecutable = Array.isArray(body.unexecutable) ? body.unexecutable : [];
279568
279667
  const warnings = Array.isArray(body.warnings) ? body.warnings : [];
279569
- 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."));
279570
279669
  if (unexecutable.length > 0) {
279571
- console.error(import_picocolors2.default.red(` Unexecutable (would fail on existing rows):`));
279572
- 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)));
279573
279672
  }
279574
279673
  if (warnings.length > 0) {
279575
- console.error(import_picocolors2.default.yellow(` Destructive (data loss):`));
279576
- 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)));
279577
279676
  }
279578
- 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.`));
279579
279678
  } else if (status2 === 403) {
279580
- console.error(import_picocolors2.default.red(` Forbidden: ${body.message ?? body.reason ?? "key lacks schema:push scope"}`));
279679
+ console.error(import_picocolors3.default.red(` Forbidden: ${body.message ?? body.reason ?? "key lacks schema:push scope"}`));
279581
279680
  } else {
279582
- console.error(import_picocolors2.default.red(` Push failed (${status2}): ${body.message ?? body.reason ?? bodyText}`));
279681
+ console.error(import_picocolors3.default.red(` Push failed (${status2}): ${body.message ?? body.reason ?? bodyText}`));
279583
279682
  }
279584
279683
  process.exit(1);
279585
279684
  }
@@ -279627,8 +279726,8 @@ function planFor(schema, targetSchema = "public") {
279627
279726
  }
279628
279727
  var log = {
279629
279728
  info: (msg, fields) => console.log(`[migrate] ${msg}`, fields),
279630
- warn: (msg, fields) => console.warn(import_picocolors3.default.yellow(`[migrate] ${msg}`), fields),
279631
- 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)
279632
279731
  };
279633
279732
  var PG_LOCK_NOT_AVAILABLE = "55P03";
279634
279733
  var LOCK_TIMEOUT = process.env.ABLO_SCHEMA_LOCK_TIMEOUT ?? process.env.ABLO_DDL_LOCK_TIMEOUT ?? "5s";
@@ -279703,7 +279802,7 @@ async function migrate(argv) {
279703
279802
  try {
279704
279803
  args = parseMigrateArgs(argv);
279705
279804
  } catch (err) {
279706
- 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)}`));
279707
279806
  process.exit(1);
279708
279807
  }
279709
279808
  const schema = await loadSchema(args.schemaPath, args.exportName);
@@ -279714,11 +279813,11 @@ async function migrate(argv) {
279714
279813
  ].join("\n");
279715
279814
  const totalStatements = plan.statements.length + plan.concurrent.length;
279716
279815
  console.log(
279717
- ` ${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`)}`
279718
279817
  );
279719
279818
  if (args.outputFile) {
279720
- (0, import_fs4.writeFileSync)(args.outputFile, sql + "\n");
279721
- 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)}`);
279722
279821
  return;
279723
279822
  }
279724
279823
  if (args.dryRun) {
@@ -279727,100 +279826,23 @@ async function migrate(argv) {
279727
279826
  }
279728
279827
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
279729
279828
  if (!dbUrl) {
279730
- 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."));
279731
279830
  process.exit(1);
279732
279831
  }
279733
- const effectiveUrl = await ensureScopedRole(dbUrl);
279832
+ const effectiveUrl = await ensureScopedRoleInteractive(dbUrl);
279734
279833
  try {
279735
279834
  await applyStatements(effectiveUrl, args.targetSchema, plan.statements, plan.concurrent);
279736
- console.log(` ${import_picocolors3.default.green("\u2713")} Migration complete`);
279835
+ console.log(` ${import_picocolors4.default.green("\u2713")} Migration complete`);
279737
279836
  } catch {
279738
279837
  process.exit(1);
279739
279838
  }
279740
279839
  }
279741
- async function ensureScopedRole(dbUrl) {
279742
- let safety;
279743
- try {
279744
- const sql = src_default(dbUrl, { max: 1, prepare: false, onnotice: () => {
279745
- } });
279746
- try {
279747
- safety = await detectRoleSafety(sql);
279748
- } finally {
279749
- await sql.end({ timeout: 5 });
279750
- }
279751
- } catch {
279752
- return dbUrl;
279753
- }
279754
- if (!safety.unsafe) return dbUrl;
279755
- const why = safety.superuser ? "a superuser" : "BYPASSRLS";
279756
- console.log(
279757
- `
279758
- ${import_picocolors3.default.yellow("!")} DATABASE_URL connects as ${import_picocolors3.default.bold(safety.role)} \u2014 ${why}, so row-level security can't be enforced.
279759
- Ablo's server will refuse this connection (${import_picocolors3.default.bold("database_role_cannot_enforce_rls")}).`
279760
- );
279761
- if (!process.stdout.isTTY) {
279762
- console.log(
279763
- import_picocolors3.default.dim(
279764
- ` Create a scoped role and update DATABASE_URL \u2014 run \`npx ablo migrate\` interactively
279765
- to do it automatically, or see https://docs.abloatai.com/quickstart#scoped-role`
279766
- )
279767
- );
279768
- return dbUrl;
279769
- }
279770
- const proceed = await ye({
279771
- message: `Create a scoped role ${DEFAULT_SCOPED_ROLE} (NOSUPERUSER, NOBYPASSRLS) and update DATABASE_URL?`,
279772
- initialValue: true
279773
- });
279774
- if (pD(proceed) || !proceed) {
279775
- console.log(import_picocolors3.default.dim(" Skipped \u2014 see https://docs.abloatai.com/quickstart#scoped-role for the manual recipe."));
279776
- return dbUrl;
279777
- }
279778
- const { role, databaseUrl } = await createScopedRole(dbUrl);
279779
- const where = persistDatabaseUrl(databaseUrl);
279780
- console.log(
279781
- ` ${import_picocolors3.default.green("\u2713")} Created role ${import_picocolors3.default.bold(role)} and updated ${import_picocolors3.default.bold("DATABASE_URL")} in ${import_picocolors3.default.bold(where)}.
279782
- ` + import_picocolors3.default.dim(` The owner credential never left this machine; the new password was written, not printed.`)
279783
- );
279784
- return databaseUrl;
279785
- }
279786
- function persistDatabaseUrl(databaseUrl, cwd = process.cwd()) {
279787
- const line = `DATABASE_URL=${databaseUrl}`;
279788
- for (const name of [".env.local", ".env"]) {
279789
- const path = (0, import_path3.resolve)(cwd, name);
279790
- if (!(0, import_fs4.existsSync)(path)) continue;
279791
- const content = (0, import_fs4.readFileSync)(path, "utf8");
279792
- if (/^DATABASE_URL=/m.test(content)) {
279793
- (0, import_fs4.writeFileSync)(path, content.replace(/^DATABASE_URL=.*$/m, line));
279794
- return name;
279795
- }
279796
- }
279797
- const envLocal = (0, import_path3.resolve)(cwd, ".env.local");
279798
- if ((0, import_fs4.existsSync)(envLocal)) {
279799
- const content = (0, import_fs4.readFileSync)(envLocal, "utf8");
279800
- (0, import_fs4.appendFileSync)(envLocal, `${content.endsWith("\n") || content.length === 0 ? "" : "\n"}${line}
279801
- `);
279802
- } else {
279803
- (0, import_fs4.writeFileSync)(envLocal, `${line}
279804
- `, { mode: 384 });
279805
- }
279806
- const gitignorePath = (0, import_path3.resolve)(cwd, ".gitignore");
279807
- const gitignore = (0, import_fs4.existsSync)(gitignorePath) ? (0, import_fs4.readFileSync)(gitignorePath, "utf8") : "";
279808
- if (!/^(\.env\.local|\.env\*|\.env\.\*|\.env.*)$/m.test(gitignore)) {
279809
- (0, import_fs4.writeFileSync)(
279810
- gitignorePath,
279811
- `${gitignore.endsWith("\n") || gitignore.length === 0 ? gitignore : `${gitignore}
279812
- `}.env.local
279813
- `
279814
- );
279815
- }
279816
- return ".env.local";
279817
- }
279818
279840
 
279819
279841
  // src/cli/generate.ts
279820
279842
  init_cjs_shims();
279821
- var import_fs5 = require("fs");
279843
+ var import_fs6 = require("fs");
279822
279844
  var import_path4 = require("path");
279823
- var import_picocolors4 = __toESM(require_picocolors(), 1);
279845
+ var import_picocolors5 = __toESM(require_picocolors(), 1);
279824
279846
  var import_schema3 = require("@abloatai/ablo/schema");
279825
279847
  var DEFAULT_SCHEMA_PATH3 = "ablo/schema.ts";
279826
279848
  var DEFAULT_EXPORT3 = "schema";
@@ -279852,7 +279874,7 @@ async function generate(argv) {
279852
279874
  try {
279853
279875
  args = parseGenerateArgs(argv);
279854
279876
  } catch (err) {
279855
- 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)}`));
279856
279878
  process.exit(1);
279857
279879
  }
279858
279880
  let source;
@@ -279861,19 +279883,19 @@ async function generate(argv) {
279861
279883
  const schemaJson = JSON.parse((0, import_schema3.serializeSchema)(schema));
279862
279884
  source = (0, import_schema3.generateTypes)(schemaJson);
279863
279885
  } catch (err) {
279864
- 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)}`));
279865
279887
  process.exit(1);
279866
279888
  }
279867
279889
  const abs = (0, import_path4.resolve)(process.cwd(), args.out);
279868
- (0, import_fs5.mkdirSync)((0, import_path4.dirname)(abs), { recursive: true });
279869
- (0, import_fs5.writeFileSync)(abs, source);
279870
- console.log(` ${import_picocolors4.default.green("\u2713")} Generated types \u2192 ${import_picocolors4.default.bold(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)}`);
279871
279893
  }
279872
279894
 
279873
279895
  // src/cli/dev.ts
279874
279896
  init_cjs_shims();
279875
- var import_picocolors5 = __toESM(require_picocolors(), 1);
279876
- var import_fs6 = require("fs");
279897
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
279898
+ var import_fs7 = require("fs");
279877
279899
  var import_path5 = require("path");
279878
279900
  var import_schema4 = require("@abloatai/ablo/schema");
279879
279901
 
@@ -279895,7 +279917,7 @@ function parseDevArgs(argv) {
279895
279917
  let schemaPath = DEFAULT_SCHEMA_PATH;
279896
279918
  let exportName = DEFAULT_EXPORT;
279897
279919
  let url = process.env.ABLO_API_URL ?? DEFAULT_URL;
279898
- let watchEnabled = true;
279920
+ let watchEnabled = false;
279899
279921
  for (let i = 0; i < argv.length; i++) {
279900
279922
  const arg = argv[i];
279901
279923
  switch (arg) {
@@ -279908,6 +279930,9 @@ function parseDevArgs(argv) {
279908
279930
  case "--url":
279909
279931
  url = argv[++i] ?? url;
279910
279932
  break;
279933
+ case "--watch":
279934
+ watchEnabled = true;
279935
+ break;
279911
279936
  case "--no-watch":
279912
279937
  watchEnabled = false;
279913
279938
  break;
@@ -279922,58 +279947,58 @@ function classifyKey(apiKey) {
279922
279947
  if (!apiKey) {
279923
279948
  return {
279924
279949
  ok: false,
279925
- 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")}.`
279926
279951
  };
279927
279952
  }
279928
279953
  if (apiKey.startsWith("sk_test_")) return { ok: true };
279929
279954
  if (apiKey.startsWith("sk_live_")) {
279930
279955
  return {
279931
279956
  ok: false,
279932
- 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.`
279933
279958
  };
279934
279959
  }
279935
279960
  if (apiKey.startsWith("rk_")) {
279936
279961
  return {
279937
279962
  ok: false,
279938
- 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.`
279939
279964
  };
279940
279965
  }
279941
- 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")}).` };
279942
279967
  }
279943
279968
  function wireEnvLocal(apiKey, cwd = process.cwd()) {
279944
279969
  const envPath = (0, import_path5.resolve)(cwd, ".env.local");
279945
279970
  const line = `ABLO_API_KEY=${apiKey}`;
279946
279971
  let action;
279947
- if (!(0, import_fs6.existsSync)(envPath)) {
279948
- (0, import_fs6.writeFileSync)(envPath, `${line}
279972
+ if (!(0, import_fs7.existsSync)(envPath)) {
279973
+ (0, import_fs7.writeFileSync)(envPath, `${line}
279949
279974
  `, { mode: 384 });
279950
- 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")}`;
279951
279976
  } else {
279952
- const content = (0, import_fs6.readFileSync)(envPath, "utf8");
279977
+ const content = (0, import_fs7.readFileSync)(envPath, "utf8");
279953
279978
  const match = content.match(/^ABLO_API_KEY=(.*)$/m);
279954
279979
  if (!match) {
279955
- (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}
279956
279981
  `);
279957
- 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")}`;
279958
279983
  } else if (match[1] === apiKey) {
279959
- action = `${import_picocolors5.default.bold(".env.local")} already has this key`;
279984
+ action = `${import_picocolors6.default.bold(".env.local")} already has this key`;
279960
279985
  } else {
279961
- (0, import_fs6.writeFileSync)(envPath, content.replace(/^ABLO_API_KEY=.*$/m, line));
279962
- 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)`)}`;
279963
279988
  }
279964
279989
  }
279965
279990
  const gitignorePath = (0, import_path5.resolve)(cwd, ".gitignore");
279966
- const gitignore = (0, import_fs6.existsSync)(gitignorePath) ? (0, import_fs6.readFileSync)(gitignorePath, "utf8") : "";
279991
+ const gitignore = (0, import_fs7.existsSync)(gitignorePath) ? (0, import_fs7.readFileSync)(gitignorePath, "utf8") : "";
279967
279992
  const ignored = /^(\.env\.local|\.env\*|\.env\.\*|\.env.*)$/m.test(gitignore);
279968
279993
  let gitignoreNote = "";
279969
279994
  if (!ignored) {
279970
- (0, import_fs6.writeFileSync)(
279995
+ (0, import_fs7.writeFileSync)(
279971
279996
  gitignorePath,
279972
279997
  `${gitignore.endsWith("\n") || gitignore.length === 0 ? gitignore : `${gitignore}
279973
279998
  `}.env.local
279974
279999
  `
279975
280000
  );
279976
- 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.`;
279977
280002
  }
279978
280003
  return `${action}.${gitignoreNote}`;
279979
280004
  }
@@ -279988,7 +280013,7 @@ async function runPush(schema, args) {
279988
280013
  if (ok) {
279989
280014
  return {
279990
280015
  ok: true,
279991
- 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})`)}`
279992
280017
  };
279993
280018
  }
279994
280019
  if (status2 === 409) {
@@ -279996,19 +280021,19 @@ async function runPush(schema, args) {
279996
280021
  const warnings = Array.isArray(body.warnings) ? body.warnings : [];
279997
280022
  const lines = [
279998
280023
  "Incompatible schema change \u2014 not safe to apply as-is.",
279999
- ...unexecutable.map((u2) => import_picocolors5.default.red(fmtSignal(u2))),
280000
- ...warnings.map((w2) => import_picocolors5.default.yellow(fmtSignal(w2))),
280001
- 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.`)
280002
280027
  ];
280003
280028
  return { ok: false, message: lines.join("\n") };
280004
280029
  }
280005
280030
  if (status2 === 403) {
280006
280031
  const serverSays = body.message ?? body.reason;
280007
- const hint = body.code === "database_role_cannot_enforce_rls" ? `Run ${import_picocolors5.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_picocolors5.default.bold("sandbox")} key with ${import_picocolors5.default.bold("schema:push")} \u2014 manage keys at ${import_picocolors5.default.cyan("https://abloatai.com")}.`;
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")}.`;
280008
280033
  return {
280009
280034
  ok: false,
280010
280035
  message: `${serverSays ?? "This key can't author schema (missing schema:push scope)."}
280011
- ` + import_picocolors5.default.dim(hint)
280036
+ ` + import_picocolors6.default.dim(hint)
280012
280037
  };
280013
280038
  }
280014
280039
  return { ok: false, message: `Push failed (${status2}): ${body.message ?? body.reason ?? bodyText}` };
@@ -280018,25 +280043,27 @@ async function dev(argv) {
280018
280043
  try {
280019
280044
  args = parseDevArgs(argv);
280020
280045
  } catch (err) {
280021
- 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)}`));
280022
280047
  process.exit(1);
280023
280048
  }
280024
280049
  if (!args.apiKey) args.apiKey = resolveApiKey("sandbox");
280025
280050
  const key = classifyKey(args.apiKey);
280026
280051
  if (!key.ok) {
280027
- console.error(import_picocolors5.default.red(` ${key.reason}`));
280052
+ console.error(import_picocolors6.default.red(` ${key.reason}`));
280028
280053
  process.exit(1);
280029
280054
  }
280030
280055
  console.log(`
280031
- ${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)")}
280032
280057
  `);
280058
+ const projectDbUrl = readProjectDatabaseUrl();
280059
+ if (projectDbUrl) await ensureScopedRoleInteractive(projectDbUrl);
280033
280060
  const schema = await loadSchema(args.schemaPath, args.exportName);
280034
280061
  const modelCount = Object.keys(schema.models).length;
280035
280062
  console.log(
280036
- ` ${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)})`)}`
280037
280064
  );
280038
- console.log(` ${import_picocolors5.default.dim("key")} ${args.apiKey.slice(0, 12)}\u2026`);
280039
- 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}
280040
280067
  `);
280041
280068
  const s = Y2();
280042
280069
  s.start("Pushing schema definition (sandbox)");
@@ -280045,20 +280072,20 @@ async function dev(argv) {
280045
280072
  if (!first.ok) process.exit(1);
280046
280073
  if (process.env.ABLO_API_KEY) {
280047
280074
  console.log(`
280048
- ${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.`);
280049
280076
  } else {
280050
280077
  console.log(`
280051
- ${import_picocolors5.default.green("\u2713")} ${wireEnvLocal(args.apiKey)}`);
280052
- 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")}`);
280053
280080
  }
280054
280081
  console.log(` Your app is wired for the sandbox.`);
280055
280082
  if (!args.watch) return;
280056
280083
  const abs = (0, import_path5.resolve)(process.cwd(), args.schemaPath);
280057
- console.log(` ${import_picocolors5.default.dim(`watching ${args.schemaPath} \u2026 (Ctrl-C to stop)`)}
280084
+ console.log(` ${import_picocolors6.default.dim(`watching ${args.schemaPath} \u2026 (Ctrl-C to stop)`)}
280058
280085
  `);
280059
280086
  let timer2 = null;
280060
280087
  let pushing = false;
280061
- const watcher = (0, import_fs6.watch)(abs, () => {
280088
+ const watcher = (0, import_fs7.watch)(abs, () => {
280062
280089
  if (timer2) clearTimeout(timer2);
280063
280090
  timer2 = setTimeout(() => {
280064
280091
  void rePush();
@@ -280074,7 +280101,7 @@ async function dev(argv) {
280074
280101
  const r2 = await runPush(next, args);
280075
280102
  s2.stop(r2.message, r2.ok ? 0 : 1);
280076
280103
  } catch (err) {
280077
- 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);
280078
280105
  } finally {
280079
280106
  pushing = false;
280080
280107
  }
@@ -280082,7 +280109,7 @@ async function dev(argv) {
280082
280109
  const stop = () => {
280083
280110
  watcher.close();
280084
280111
  console.log(`
280085
- ${import_picocolors5.default.dim("stopped.")}`);
280112
+ ${import_picocolors6.default.dim("stopped.")}`);
280086
280113
  process.exit(0);
280087
280114
  };
280088
280115
  process.on("SIGINT", stop);
@@ -280094,7 +280121,7 @@ async function dev(argv) {
280094
280121
  // src/cli/login.ts
280095
280122
  init_cjs_shims();
280096
280123
  var import_child_process = require("child_process");
280097
- var import_picocolors6 = __toESM(require_picocolors(), 1);
280124
+ var import_picocolors7 = __toESM(require_picocolors(), 1);
280098
280125
  var CLIENT_ID = "ablo-cli";
280099
280126
  var AUTH_URL = (process.env.ABLO_AUTH_URL ?? "https://www.abloatai.com").replace(/\/+$/, "");
280100
280127
  var sleep = (ms) => new Promise((r2) => setTimeout(r2, ms));
@@ -280110,16 +280137,21 @@ function openBrowser(url) {
280110
280137
  }
280111
280138
  async function deviceLogin() {
280112
280139
  Ie(`${brand("ablo")} login`);
280113
- const account = await ve({
280114
- message: "Ablo account",
280115
- options: [
280116
- { value: "login", label: "Log in to an existing account" },
280117
- { value: "signup", label: "Create a new account" }
280118
- ]
280119
- });
280120
- if (pD(account)) {
280121
- xe("Cancelled.");
280122
- 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;
280123
280155
  }
280124
280156
  const codeRes = await fetch(`${AUTH_URL}/api/auth/device/code`, {
280125
280157
  method: "POST",
@@ -280133,9 +280165,9 @@ async function deviceLogin() {
280133
280165
  const code = await codeRes.json();
280134
280166
  const approvePath = `/cli?user_code=${code.user_code}`;
280135
280167
  const url = account === "signup" ? `${AUTH_URL}/signup?next=${encodeURIComponent(approvePath)}` : code.verification_uri_complete ?? code.verification_uri;
280136
- Me(`${import_picocolors6.default.bold(code.user_code)}
280168
+ Me(`${import_picocolors7.default.bold(code.user_code)}
280137
280169
 
280138
- ${import_picocolors6.default.dim(url)}`, "Approve in your browser");
280170
+ ${import_picocolors7.default.dim(url)}`, "Approve in your browser");
280139
280171
  openBrowser(url);
280140
280172
  const s = Y2();
280141
280173
  s.start("Waiting for approval\u2026");
@@ -280193,7 +280225,7 @@ ${import_picocolors6.default.dim(url)}`, "Approve in your browser");
280193
280225
  if (reason) M2.error(reason);
280194
280226
  else if (provRes) M2.error(`Key provisioning returned ${provRes.status} from ${AUTH_URL}/api/cli/provision-key.`);
280195
280227
  M2.error(
280196
- `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")}.`
280197
280229
  );
280198
280230
  process.exit(1);
280199
280231
  }
@@ -280209,7 +280241,7 @@ ${import_picocolors6.default.dim(url)}`, "Approve in your browser");
280209
280241
  ...prov.live ? { production: entry(prov.live) } : {}
280210
280242
  });
280211
280243
  s.stop(`Saved keys to ${path}`);
280212
- 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.`);
280213
280245
  }
280214
280246
  async function login() {
280215
280247
  await deviceLogin();
@@ -280217,20 +280249,20 @@ async function login() {
280217
280249
  function logout() {
280218
280250
  const removed = clearCredential();
280219
280251
  if (removed) {
280220
- 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()})`)}`);
280221
280253
  } else {
280222
- 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.`);
280223
280255
  }
280224
280256
  if (process.env.ABLO_API_KEY) {
280225
280257
  console.log(
280226
- 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.`)
280227
280259
  );
280228
280260
  }
280229
280261
  }
280230
280262
 
280231
280263
  // src/cli/mode.ts
280232
280264
  init_cjs_shims();
280233
- var import_picocolors7 = __toESM(require_picocolors(), 1);
280265
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
280234
280266
  var PREFIX = { sandbox: "sk_test_", production: "rk_live_" };
280235
280267
  function hintFor(m2, current) {
280236
280268
  const parts = [];
@@ -280240,10 +280272,10 @@ function hintFor(m2, current) {
280240
280272
  }
280241
280273
  function apply(m2) {
280242
280274
  setMode(m2);
280243
- 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)}`);
280244
280276
  if (!getKeyEntry(m2)) {
280245
280277
  console.log(
280246
- 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`)}.`)
280247
280279
  );
280248
280280
  }
280249
280281
  }
@@ -280256,14 +280288,14 @@ async function mode(argv) {
280256
280288
  }
280257
280289
  if (arg) {
280258
280290
  console.error(
280259
- 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")})`)
280260
280292
  );
280261
280293
  process.exit(1);
280262
280294
  }
280263
280295
  const current = getMode();
280264
280296
  if (!process.stdin.isTTY || process.env.CI) {
280265
280297
  console.error(
280266
- 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})`)
280267
280299
  );
280268
280300
  process.exit(1);
280269
280301
  }
@@ -280284,13 +280316,13 @@ async function mode(argv) {
280284
280316
 
280285
280317
  // src/cli/status.ts
280286
280318
  init_cjs_shims();
280287
- var import_picocolors8 = __toESM(require_picocolors(), 1);
280319
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
280288
280320
  function expiryLabel(iso) {
280289
280321
  const ms = Date.parse(iso) - Date.now();
280290
280322
  if (Number.isNaN(ms)) return "";
280291
- if (ms <= 0) return import_picocolors8.default.red("expired");
280323
+ if (ms <= 0) return import_picocolors9.default.red("expired");
280292
280324
  const days = Math.floor(ms / (24 * 60 * 60 * 1e3));
280293
- 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");
280294
280326
  }
280295
280327
  async function ping(apiUrl) {
280296
280328
  const ctrl = new AbortController();
@@ -280309,36 +280341,36 @@ async function status() {
280309
280341
  const cfg = readConfig();
280310
280342
  const mode2 = getMode();
280311
280343
  console.log(`
280312
- ${brand("ablo")} ${import_picocolors8.default.dim("status")}
280344
+ ${brand("ablo")} ${import_picocolors9.default.dim("status")}
280313
280345
  `);
280314
280346
  if (process.env.ABLO_API_KEY) {
280315
280347
  console.log(
280316
- ` ${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)")}`
280317
280349
  );
280318
280350
  } else if (!cfg) {
280319
- 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")}.`);
280320
280352
  }
280321
- 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)}`);
280322
280354
  for (const m2 of ["sandbox", "production"]) {
280323
280355
  const entry = getKeyEntry(m2);
280324
- 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");
280325
280357
  if (entry) {
280326
280358
  const exp = entry.expiresAt ? ` ${expiryLabel(entry.expiresAt)}` : "";
280327
- 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}`);
280328
280360
  } else {
280329
- 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")}`);
280330
280362
  }
280331
280363
  }
280332
280364
  const org = getKeyEntry(mode2)?.organizationId;
280333
- if (org) console.log(` ${import_picocolors8.default.dim("org")} ${org}`);
280334
- process.stdout.write(` ${import_picocolors8.default.dim("api")} ${apiUrl} `);
280335
- 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"));
280336
280368
  console.log();
280337
280369
  }
280338
280370
 
280339
280371
  // src/cli/logs.ts
280340
280372
  init_cjs_shims();
280341
- var import_picocolors9 = __toESM(require_picocolors(), 1);
280373
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
280342
280374
  function parseLogsArgs(argv) {
280343
280375
  const args = {
280344
280376
  follow: true,
@@ -280402,10 +280434,10 @@ function resolveSince(since) {
280402
280434
  var sleep2 = (ms) => new Promise((r2) => setTimeout(r2, ms));
280403
280435
  function colorOp(op) {
280404
280436
  const label = op.padEnd(6);
280405
- if (op === "create") return import_picocolors9.default.green(label);
280406
- if (op === "update") return import_picocolors9.default.yellow(label);
280407
- if (op === "delete") return import_picocolors9.default.red(label);
280408
- 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);
280409
280441
  }
280410
280442
  function render(e2, json) {
280411
280443
  if (json) {
@@ -280414,21 +280446,21 @@ function render(e2, json) {
280414
280446
  return;
280415
280447
  }
280416
280448
  const t = new Date(e2.at).toLocaleTimeString();
280417
- const actor = e2.actor ? import_picocolors9.default.dim(` ${e2.actor}`) : "";
280418
- 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}`);
280419
280451
  }
280420
280452
  async function logs(argv) {
280421
280453
  let args;
280422
280454
  try {
280423
280455
  args = parseLogsArgs(argv);
280424
280456
  } catch (err) {
280425
- 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)}`));
280426
280458
  process.exit(1);
280427
280459
  }
280428
280460
  const apiKey = resolveApiKey(args.mode);
280429
280461
  if (!apiKey) {
280430
280462
  console.error(
280431
- 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")}.`)
280432
280464
  );
280433
280465
  process.exit(1);
280434
280466
  }
@@ -280442,7 +280474,7 @@ async function logs(argv) {
280442
280474
  if (!res) return null;
280443
280475
  if (!res.ok) {
280444
280476
  const body = await res.json().catch(() => ({}));
280445
- 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 ?? ""}`));
280446
280478
  process.exit(1);
280447
280479
  }
280448
280480
  const json = await res.json();
@@ -280453,7 +280485,7 @@ async function logs(argv) {
280453
280485
  }
280454
280486
  if (!args.json) {
280455
280487
  console.log(`
280456
- ${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)`)}
280457
280489
  `);
280458
280490
  }
280459
280491
  const initial = await fetchPage({
@@ -280463,13 +280495,13 @@ async function logs(argv) {
280463
280495
  ...args.op ? { op: args.op } : {}
280464
280496
  });
280465
280497
  if (!initial) {
280466
- console.error(import_picocolors9.default.red(` Couldn't reach ${baseUrl2}.`));
280498
+ console.error(import_picocolors10.default.red(` Couldn't reach ${baseUrl2}.`));
280467
280499
  process.exit(1);
280468
280500
  }
280469
280501
  for (const e2 of initial.events) render(e2, args.json);
280470
280502
  let cursor = initial.cursor;
280471
280503
  if (!args.follow) return;
280472
- 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)")}
280473
280505
  `);
280474
280506
  for (; ; ) {
280475
280507
  await sleep2(1500);
@@ -280486,8 +280518,8 @@ async function logs(argv) {
280486
280518
 
280487
280519
  // src/cli/webhooks.ts
280488
280520
  init_cjs_shims();
280489
- var import_fs7 = require("fs");
280490
- var import_picocolors10 = __toESM(require_picocolors(), 1);
280521
+ var import_fs8 = require("fs");
280522
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
280491
280523
  var ENV_KEY = "ABLO_WEBHOOK_SECRET";
280492
280524
  function flag(args, name) {
280493
280525
  const inline = args.find((a) => a.startsWith(`${name}=`));
@@ -280514,12 +280546,12 @@ function requireKey(mode2) {
280514
280546
  const apiKey = resolveApiKey(mode2);
280515
280547
  if (!apiKey) {
280516
280548
  console.error(
280517
- 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")}.`)
280518
280550
  );
280519
280551
  process.exit(1);
280520
280552
  }
280521
280553
  if (!apiKey.startsWith("sk_")) {
280522
- 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_)."));
280523
280555
  process.exit(1);
280524
280556
  }
280525
280557
  return apiKey;
@@ -280535,22 +280567,22 @@ async function api(apiKey, method, path, body) {
280535
280567
  ...body ? { body: JSON.stringify(body) } : {}
280536
280568
  }).catch(() => null);
280537
280569
  if (!res) {
280538
- console.error(import_picocolors10.default.red(` Couldn't reach ${baseUrl()}.`));
280570
+ console.error(import_picocolors11.default.red(` Couldn't reach ${baseUrl()}.`));
280539
280571
  process.exit(1);
280540
280572
  }
280541
280573
  if (!res.ok) {
280542
280574
  const err = await res.json().catch(() => ({}));
280543
- 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 ?? ""}`));
280544
280576
  process.exit(1);
280545
280577
  }
280546
280578
  return await res.json();
280547
280579
  }
280548
280580
  function writeSecretToEnv(secret) {
280549
- 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";
280550
280582
  const line = `${ENV_KEY}=${secret}`;
280551
280583
  let next;
280552
- if ((0, import_fs7.existsSync)(file)) {
280553
- 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");
280554
280586
  next = new RegExp(`^${ENV_KEY}=.*$`, "m").test(existing) ? existing.replace(new RegExp(`^${ENV_KEY}=.*$`, "m"), line) : `${existing.replace(/\n*$/, "")}
280555
280587
  ${line}
280556
280588
  `;
@@ -280558,15 +280590,15 @@ ${line}
280558
280590
  next = `${line}
280559
280591
  `;
280560
280592
  }
280561
- (0, import_fs7.writeFileSync)(file, next);
280593
+ (0, import_fs8.writeFileSync)(file, next);
280562
280594
  return file;
280563
280595
  }
280564
280596
  function printEndpoint(e2) {
280565
- const dot = e2.status === "enabled" ? import_picocolors10.default.green("\u25CF") : import_picocolors10.default.red("\u25CF");
280566
- const health = e2.last_error ? import_picocolors10.default.red(` last error: ${e2.last_error}`) : "";
280567
- 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}`);
280568
280600
  console.log(
280569
- import_picocolors10.default.dim(
280601
+ import_picocolors11.default.dim(
280570
280602
  ` ${e2.status} \xB7 ${e2.environment} \xB7 events ${e2.enabled_events.join(",")} \xB7 cursor ${e2.cursor ?? "\u2014"}${health}`
280571
280603
  )
280572
280604
  );
@@ -280578,7 +280610,7 @@ async function webhooks(argv) {
280578
280610
  if (sub === "create") {
280579
280611
  const url = positional(rest);
280580
280612
  if (!url) {
280581
- 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>"));
280582
280614
  process.exit(1);
280583
280615
  }
280584
280616
  const apiKey = requireKey(mode2);
@@ -280590,8 +280622,8 @@ async function webhooks(argv) {
280590
280622
  });
280591
280623
  const file = writeSecretToEnv(created.secret);
280592
280624
  console.log(`
280593
- ${import_picocolors10.default.green("\u2713")} Registered ${import_picocolors10.default.bold(created.id)} \u2192 ${created.url}`);
280594
- 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)")}
280595
280627
  `);
280596
280628
  return;
280597
280629
  }
@@ -280599,7 +280631,7 @@ async function webhooks(argv) {
280599
280631
  const apiKey = requireKey(mode2);
280600
280632
  const { data } = await api(apiKey, "GET", "");
280601
280633
  if (data.length === 0) {
280602
- 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>"));
280603
280635
  return;
280604
280636
  }
280605
280637
  console.log();
@@ -280610,40 +280642,40 @@ async function webhooks(argv) {
280610
280642
  if (sub === "roll") {
280611
280643
  const id = positional(rest);
280612
280644
  if (!id) {
280613
- 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>"));
280614
280646
  process.exit(1);
280615
280647
  }
280616
280648
  const apiKey = requireKey(mode2);
280617
280649
  const rolled = await api(apiKey, "POST", `/${id}/roll_secret`);
280618
280650
  const file = writeSecretToEnv(rolled.secret);
280619
280651
  console.log(`
280620
- ${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)")}
280621
280653
  `);
280622
280654
  return;
280623
280655
  }
280624
280656
  if (sub === "enable") {
280625
280657
  const id = positional(rest);
280626
280658
  if (!id) {
280627
- 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>"));
280628
280660
  process.exit(1);
280629
280661
  }
280630
280662
  const apiKey = requireKey(mode2);
280631
280663
  const e2 = await api(apiKey, "POST", `/${id}/enable`);
280632
- 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)}`);
280633
280665
  return;
280634
280666
  }
280635
280667
  if (sub === "rm" || sub === "delete") {
280636
280668
  const id = positional(rest);
280637
280669
  if (!id) {
280638
- 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>"));
280639
280671
  process.exit(1);
280640
280672
  }
280641
280673
  const apiKey = requireKey(mode2);
280642
280674
  await api(apiKey, "DELETE", `/${id}`);
280643
- 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)}`);
280644
280676
  return;
280645
280677
  }
280646
- console.log(` ${import_picocolors10.default.bold("Usage:")}`);
280678
+ console.log(` ${import_picocolors11.default.bold("Usage:")}`);
280647
280679
  console.log(` ${brand("ablo webhooks create <url>")} Register an endpoint; writes ${ENV_KEY}`);
280648
280680
  console.log(` ${brand("ablo webhooks list")} List endpoints + delivery health`);
280649
280681
  console.log(` ${brand("ablo webhooks roll <id>")} Mint a fresh signing secret`);
@@ -280654,7 +280686,7 @@ async function webhooks(argv) {
280654
280686
 
280655
280687
  // src/cli/check.ts
280656
280688
  init_cjs_shims();
280657
- var import_picocolors11 = __toESM(require_picocolors(), 1);
280689
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
280658
280690
  var import_schema5 = require("@abloatai/ablo/schema");
280659
280691
  var DEFAULT_SCHEMA_PATH4 = "ablo/schema.ts";
280660
280692
  var DEFAULT_EXPORT4 = "schema";
@@ -280689,13 +280721,13 @@ async function check(argv) {
280689
280721
  try {
280690
280722
  args = parseCheckArgs(argv);
280691
280723
  } catch (err) {
280692
- 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)}`));
280693
280725
  process.exit(1);
280694
280726
  }
280695
280727
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
280696
280728
  if (!dbUrl) {
280697
280729
  console.error(
280698
- 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.`)
280699
280731
  );
280700
280732
  process.exit(1);
280701
280733
  }
@@ -280710,7 +280742,7 @@ async function check(argv) {
280710
280742
  [args.appSchema]
280711
280743
  );
280712
280744
  } catch (err) {
280713
- 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)}`));
280714
280746
  await sql.end({ timeout: 2 });
280715
280747
  process.exit(1);
280716
280748
  }
@@ -280725,7 +280757,7 @@ async function check(argv) {
280725
280757
  set.add(r2.column_name);
280726
280758
  }
280727
280759
  console.log(`
280728
- ${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}"`)}
280729
280761
  `);
280730
280762
  const declaredTables = /* @__PURE__ */ new Set();
280731
280763
  let errors = 0;
@@ -280735,7 +280767,7 @@ async function check(argv) {
280735
280767
  declaredTables.add(table);
280736
280768
  const present = colsByTable.get(table);
280737
280769
  if (!present) {
280738
- 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")}`);
280739
280771
  errors++;
280740
280772
  continue;
280741
280773
  }
@@ -280757,26 +280789,26 @@ async function check(argv) {
280757
280789
  if (!present.has(col)) problems.push(`missing column "${col}" (field ${fieldName})`);
280758
280790
  }
280759
280791
  if (problems.length > 0) {
280760
- console.log(` ${import_picocolors11.default.red("\u2717")} ${import_picocolors11.default.bold(key)} ${import_picocolors11.default.dim("\u2192")} ${table}`);
280761
- for (const p2 of problems) console.log(` ${import_picocolors11.default.red("\u2022")} ${p2}`);
280762
- 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}`);
280763
280795
  errors++;
280764
280796
  } else if (warns.length > 0) {
280765
- console.log(` ${import_picocolors11.default.yellow("!")} ${import_picocolors11.default.bold(key)} ${import_picocolors11.default.dim("\u2192")} ${table}`);
280766
- 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}`);
280767
280799
  warnings++;
280768
280800
  } else {
280769
- 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)`)}`);
280770
280802
  }
280771
280803
  }
280772
280804
  const modelCount = Object.keys(schemaJson.models).length;
280773
280805
  const ignored = [...colsByTable.keys()].filter((t) => !declaredTables.has(t)).length;
280774
280806
  console.log(
280775
280807
  `
280776
- ${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"}`)}` : "")
280777
280809
  );
280778
280810
  if (ignored > 0) {
280779
- 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`)}`);
280780
280812
  }
280781
280813
  console.log();
280782
280814
  process.exit(errors > 0 ? 1 : 0);
@@ -280784,7 +280816,7 @@ async function check(argv) {
280784
280816
 
280785
280817
  // src/cli/upgrade.ts
280786
280818
  init_cjs_shims();
280787
- var import_picocolors12 = __toESM(require_picocolors(), 1);
280819
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
280788
280820
  var import_ts_morph = __toESM(require_ts_morph(), 1);
280789
280821
  var DEFAULT_GLOBS = ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}", "ablo/**/*.{ts,tsx}", "lib/**/*.{ts,tsx}"];
280790
280822
  var VERB_ARGS = {
@@ -280862,7 +280894,7 @@ async function upgrade(argv) {
280862
280894
  project.addSourceFilesAtPaths(globs.length > 0 ? globs : DEFAULT_GLOBS);
280863
280895
  const files = project.getSourceFiles();
280864
280896
  if (files.length === 0) {
280865
- 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"`.'));
280866
280898
  return;
280867
280899
  }
280868
280900
  const edits = [];
@@ -280936,39 +280968,39 @@ async function upgrade(argv) {
280936
280968
  const rel = (f) => f.replace(cwd + "/", "");
280937
280969
  console.log();
280938
280970
  if (edits.length === 0 && manual.length === 0) {
280939
- 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."));
280940
280972
  return;
280941
280973
  }
280942
280974
  if (edits.length > 0) {
280943
- 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"}:`));
280944
280976
  for (const e2 of edits) {
280945
- console.log(` ${import_picocolors12.default.dim(`${rel(e2.file)}:${e2.line}`)} ${import_picocolors12.default.cyan(e2.rule)}`);
280946
- console.log(` ${import_picocolors12.default.red("-")} ${e2.before}`);
280947
- 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}`);
280948
280980
  }
280949
280981
  }
280950
280982
  if (manual.length > 0) {
280951
280983
  console.log();
280952
- 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):`)));
280953
280985
  for (const m2 of manual) {
280954
- console.log(` ${import_picocolors12.default.dim(`${rel(m2.file)}:${m2.line}`)} ${import_picocolors12.default.yellow(m2.rule)}`);
280955
- 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)}`);
280956
280988
  console.log(` \u2192 ${m2.hint}`);
280957
280989
  }
280958
280990
  }
280959
280991
  console.log();
280960
280992
  if (write) {
280961
280993
  await project.save();
280962
- 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.`));
280963
280995
  } else {
280964
- 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)."));
280965
280997
  }
280966
280998
  }
280967
280999
 
280968
281000
  // src/cli/pull.ts
280969
281001
  init_cjs_shims();
280970
- var import_picocolors13 = __toESM(require_picocolors(), 1);
280971
- var import_fs8 = require("fs");
281002
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
281003
+ var import_fs9 = require("fs");
280972
281004
  var DEFAULT_OUT2 = "ablo/schema.ts";
280973
281005
  var DEFAULT_IMPORT = "@abloatai/ablo/schema";
280974
281006
  var TENANCY_COLUMN = "organization_id";
@@ -281079,53 +281111,53 @@ async function pull(argv) {
281079
281111
  try {
281080
281112
  args = parsePullArgs(argv);
281081
281113
  } catch (err) {
281082
- 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)}`));
281083
281115
  process.exit(1);
281084
281116
  }
281085
281117
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
281086
281118
  if (!dbUrl) {
281087
- 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.`));
281088
281120
  process.exit(1);
281089
281121
  }
281090
- if ((0, import_fs8.existsSync)(args.out) && !args.force) {
281122
+ if ((0, import_fs9.existsSync)(args.out) && !args.force) {
281091
281123
  console.error(
281092
- 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.`)
281093
281125
  );
281094
281126
  process.exit(1);
281095
281127
  }
281096
281128
  console.log(`
281097
- ${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}"`)}
281098
281130
  `);
281099
281131
  let result;
281100
281132
  try {
281101
281133
  result = await buildSchemaSourceFromDb({ dbUrl, appSchema: args.appSchema, importPath: args.importPath });
281102
281134
  } catch (err) {
281103
- 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)}`));
281104
281136
  process.exit(1);
281105
281137
  }
281106
281138
  if (result.models.length === 0) {
281107
281139
  console.error(
281108
- 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).`)
281109
281141
  );
281110
281142
  process.exit(1);
281111
281143
  }
281112
- (0, import_fs8.writeFileSync)(args.out, result.source);
281113
- console.log(` ${import_picocolors13.default.green("\u2713")} wrote ${import_picocolors13.default.bold(args.out)} ${import_picocolors13.default.dim(`(${result.models.length} models)`)}`);
281114
- 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(", ")}`)}`);
281115
281147
  if (result.skipped > 0) {
281116
- 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`)}`);
281117
281149
  }
281118
281150
  console.log(
281119
281151
  `
281120
- ${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")}.
281121
281153
  `
281122
281154
  );
281123
281155
  }
281124
281156
 
281125
281157
  // src/cli/prisma-pull.ts
281126
281158
  init_cjs_shims();
281127
- var import_picocolors14 = __toESM(require_picocolors(), 1);
281128
- var import_fs9 = require("fs");
281159
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
281160
+ var import_fs10 = require("fs");
281129
281161
 
281130
281162
  // src/cli/schema-ir.ts
281131
281163
  init_cjs_shims();
@@ -281402,56 +281434,56 @@ async function prismaPull(argv) {
281402
281434
  try {
281403
281435
  args = parsePrismaPullArgs(argv);
281404
281436
  } catch (err) {
281405
- 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)}`));
281406
281438
  process.exit(1);
281407
281439
  }
281408
- if (!(0, import_fs9.existsSync)(args.schema)) {
281440
+ if (!(0, import_fs10.existsSync)(args.schema)) {
281409
281441
  console.error(
281410
- 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>")}.`)
281411
281443
  );
281412
281444
  process.exit(1);
281413
281445
  }
281414
- if ((0, import_fs9.existsSync)(args.out) && !args.force) {
281446
+ if ((0, import_fs10.existsSync)(args.out) && !args.force) {
281415
281447
  console.error(
281416
- 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.`)
281417
281449
  );
281418
281450
  process.exit(1);
281419
281451
  }
281420
281452
  console.log(`
281421
- ${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)}
281422
281454
  `);
281423
281455
  let result;
281424
281456
  try {
281425
- const src = (0, import_fs9.readFileSync)(args.schema, "utf8");
281457
+ const src = (0, import_fs10.readFileSync)(args.schema, "utf8");
281426
281458
  result = buildSchemaSourceFromPrisma({ src, importPath: args.importPath });
281427
281459
  } catch (err) {
281428
- 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)}`));
281429
281461
  process.exit(1);
281430
281462
  }
281431
281463
  if (result.models.length === 0) {
281432
281464
  console.error(
281433
- 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")}).`)
281434
281466
  );
281435
281467
  process.exit(1);
281436
281468
  }
281437
- (0, import_fs9.writeFileSync)(args.out, result.source);
281438
- console.log(` ${import_picocolors14.default.green("\u2713")} wrote ${import_picocolors14.default.bold(args.out)} ${import_picocolors14.default.dim(`(${result.models.length} models)`)}`);
281439
- 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(", ")}`)}`);
281440
281472
  if (result.skipped.length > 0) {
281441
- console.log(` ${import_picocolors14.default.dim(`${result.skipped.length} model(s) skipped:`)}`);
281442
- 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}`)}`);
281443
281475
  }
281444
281476
  console.log(
281445
281477
  `
281446
- ${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")}.
281447
281479
  `
281448
281480
  );
281449
281481
  }
281450
281482
 
281451
281483
  // src/cli/drizzle-pull.ts
281452
281484
  init_cjs_shims();
281453
- var import_picocolors15 = __toESM(require_picocolors(), 1);
281454
- var import_fs10 = require("fs");
281485
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
281486
+ var import_fs11 = require("fs");
281455
281487
  var import_path6 = require("path");
281456
281488
  var DEFAULT_OUT4 = "ablo/schema.ts";
281457
281489
  var DEFAULT_IMPORT3 = "@abloatai/ablo/schema";
@@ -281586,27 +281618,27 @@ async function drizzlePull(argv) {
281586
281618
  try {
281587
281619
  args = parseDrizzlePullArgs(argv);
281588
281620
  } catch (err) {
281589
- 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)}`));
281590
281622
  process.exit(1);
281591
281623
  }
281592
281624
  if (!args.schema) {
281593
281625
  console.error(
281594
- 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")}.`)
281595
281627
  );
281596
281628
  process.exit(1);
281597
281629
  }
281598
- if (!(0, import_fs10.existsSync)(args.schema)) {
281599
- 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)}.`));
281600
281632
  process.exit(1);
281601
281633
  }
281602
- if ((0, import_fs10.existsSync)(args.out) && !args.force) {
281634
+ if ((0, import_fs11.existsSync)(args.out) && !args.force) {
281603
281635
  console.error(
281604
- 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.`)
281605
281637
  );
281606
281638
  process.exit(1);
281607
281639
  }
281608
281640
  console.log(`
281609
- ${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)}
281610
281642
  `);
281611
281643
  let result;
281612
281644
  try {
@@ -281614,33 +281646,33 @@ async function drizzlePull(argv) {
281614
281646
  result = await buildSchemaSourceFromDrizzle({ mod, importPath: args.importPath });
281615
281647
  } catch (err) {
281616
281648
  const msg = err instanceof Error ? err.message : String(err);
281617
- const hint = /Cannot find package 'drizzle-orm'/.test(msg) ? import_picocolors15.default.dim(` (install ${import_picocolors15.default.bold("drizzle-orm")} in this project)`) : "";
281618
- 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);
281619
281651
  process.exit(1);
281620
281652
  }
281621
281653
  if (result.models.length === 0) {
281622
281654
  console.error(
281623
- 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).`)
281624
281656
  );
281625
281657
  process.exit(1);
281626
281658
  }
281627
- (0, import_fs10.writeFileSync)(args.out, result.source);
281628
- console.log(` ${import_picocolors15.default.green("\u2713")} wrote ${import_picocolors15.default.bold(args.out)} ${import_picocolors15.default.dim(`(${result.models.length} models)`)}`);
281629
- 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(", ")}`)}`);
281630
281662
  if (result.skipped.length > 0) {
281631
- console.log(` ${import_picocolors15.default.dim(`${result.skipped.length} table(s) skipped:`)}`);
281632
- 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}`)}`);
281633
281665
  }
281634
281666
  console.log(
281635
281667
  `
281636
- ${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")}.
281637
281669
  `
281638
281670
  );
281639
281671
  }
281640
281672
 
281641
281673
  // src/cli/index.ts
281642
281674
  var LOGO = `
281643
- ${brand("ablo")} ${import_picocolors16.default.dim("sync engine")}
281675
+ ${brand("ablo")} ${import_picocolors17.default.dim("sync engine")}
281644
281676
  `;
281645
281677
  async function main() {
281646
281678
  const command = process.argv[2];
@@ -281659,7 +281691,8 @@ async function main() {
281659
281691
  } else if (command === "webhooks") {
281660
281692
  await webhooks(process.argv.slice(3));
281661
281693
  } else if (command === "dev") {
281662
- 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"]);
281663
281696
  } else if (command === "check") {
281664
281697
  await check(process.argv.slice(3));
281665
281698
  } else if (command === "pull") {
@@ -281674,20 +281707,27 @@ async function main() {
281674
281707
  } else if (command === "migrate") {
281675
281708
  await migrate(process.argv.slice(3));
281676
281709
  } else if (command === "push") {
281677
- 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
+ }
281678
281718
  } else if (command === "upgrade") {
281679
281719
  await upgrade(process.argv.slice(3));
281680
281720
  } else if (command === "generate") {
281681
281721
  await generate(process.argv.slice(3));
281682
281722
  } else if (command === "schema") {
281683
281723
  console.error(
281684
- ` ${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")}\`.`
281685
281725
  );
281686
281726
  console.error(` Run \`ablo push${process.argv.slice(4).join(" ") ? " " + process.argv.slice(4).join(" ") : ""}\` instead.`);
281687
281727
  process.exitCode = 1;
281688
281728
  } else {
281689
281729
  console.log(LOGO);
281690
- console.log(` ${import_picocolors16.default.bold("Usage:")}`);
281730
+ console.log(` ${import_picocolors17.default.bold("Usage:")}`);
281691
281731
  console.log(` npx ablo init Scaffold ablo/ directory + starter schema`);
281692
281732
  console.log(` npx ablo init --yes [--framework nextjs] Non-interactive (agents/CI): no prompts, flag-driven`);
281693
281733
  console.log(` [--auth apikey] [--storage direct|endpoint] [--no-agent] [--no-pull] [--no-install] [--no-login]`);
@@ -281712,10 +281752,10 @@ async function main() {
281712
281752
  console.log(` npx ablo generate Emit TypeScript types from your schema`);
281713
281753
  console.log(` npx ablo generate --out path.ts Write generated types to a path`);
281714
281754
  console.log();
281715
- console.log(` ${import_picocolors16.default.bold("Schema workflow:")}`);
281755
+ console.log(` ${import_picocolors17.default.bold("Schema workflow:")}`);
281716
281756
  console.log(` The server holds its own copy of your schema \u2014 edit ${brand("ablo/schema.ts")}, then`);
281717
281757
  console.log(` run ${brand("ablo push")} (or keep ${brand("ablo dev")} running) before the server will accept`);
281718
- 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")}.`);
281719
281759
  console.log();
281720
281760
  }
281721
281761
  }
@@ -281752,7 +281792,7 @@ function parseInitArgs(args) {
281752
281792
  function detectOrm(override) {
281753
281793
  if (override === "prisma" || override === "drizzle" || override === "none") return override;
281754
281794
  try {
281755
- 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"));
281756
281796
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
281757
281797
  if (deps["@prisma/client"] || deps["prisma"]) return "prisma";
281758
281798
  if (deps["drizzle-orm"]) return "drizzle";
@@ -281783,8 +281823,8 @@ async function chooseBool(flagValue, fallback, interactive, prompt) {
281783
281823
  async function init(args = []) {
281784
281824
  const opts = parseInitArgs(args);
281785
281825
  const interactive = Boolean(process.stdin.isTTY) && !opts.yes && !process.env.CI;
281786
- Ie(`${brand("ablo")} ${import_picocolors16.default.dim("sync engine")}`);
281787
- 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")) {
281788
281828
  xe("No package.json found. Run this from your project root.");
281789
281829
  process.exit(1);
281790
281830
  }
@@ -281860,14 +281900,14 @@ async function init(args = []) {
281860
281900
  );
281861
281901
  }
281862
281902
  const abloDir = "ablo";
281863
- (0, import_fs11.mkdirSync)(abloDir, { recursive: true });
281903
+ (0, import_fs12.mkdirSync)(abloDir, { recursive: true });
281864
281904
  const created = [];
281865
281905
  let schemaSource = generateSchema();
281866
281906
  let schemaNote = "";
281867
281907
  if (pullExisting) {
281868
281908
  const dbUrl = process.env.DATABASE_URL ?? process.env.ABLO_DATABASE_URL;
281869
281909
  if (!dbUrl) {
281870
- 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)");
281871
281911
  } else {
281872
281912
  try {
281873
281913
  const pulled = await buildSchemaSourceFromDb({
@@ -281877,60 +281917,60 @@ async function init(args = []) {
281877
281917
  });
281878
281918
  if (pulled.models.length > 0) {
281879
281919
  schemaSource = pulled.source;
281880
- schemaNote = import_picocolors16.default.dim(` (pulled ${pulled.models.length} models)`);
281920
+ schemaNote = import_picocolors17.default.dim(` (pulled ${pulled.models.length} models)`);
281881
281921
  } else {
281882
- schemaNote = import_picocolors16.default.dim(" (no adoptable tables \u2014 wrote starter)");
281922
+ schemaNote = import_picocolors17.default.dim(" (no adoptable tables \u2014 wrote starter)");
281883
281923
  }
281884
281924
  } catch {
281885
- schemaNote = import_picocolors16.default.dim(" (pull failed \u2014 wrote starter)");
281925
+ schemaNote = import_picocolors17.default.dim(" (pull failed \u2014 wrote starter)");
281886
281926
  }
281887
281927
  }
281888
281928
  }
281889
- (0, import_fs11.writeFileSync)((0, import_path7.join)(abloDir, "schema.ts"), schemaSource);
281929
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "schema.ts"), schemaSource);
281890
281930
  created.push(`${abloDir}/schema.ts${schemaNote}`);
281891
- (0, import_fs11.writeFileSync)((0, import_path7.join)(abloDir, "index.ts"), generateSyncConfig(auth, storage));
281931
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "index.ts"), generateSyncConfig(auth, storage));
281892
281932
  created.push(`${abloDir}/index.ts`);
281893
281933
  const orm = detectOrm(opts.orm);
281894
281934
  if (storage === "endpoint") {
281895
- (0, import_fs11.writeFileSync)((0, import_path7.join)(abloDir, "data-source.ts"), generateDataSource(orm));
281935
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "data-source.ts"), generateDataSource(orm));
281896
281936
  created.push(`${abloDir}/data-source.ts${orm === "drizzle" ? " (Drizzle)" : " (Prisma)"}`);
281897
281937
  }
281898
281938
  const envFile = framework === "nextjs" ? ".env.local" : ".env";
281899
- if (!(0, import_fs11.existsSync)(envFile)) {
281900
- (0, import_fs11.writeFileSync)(envFile, generateEnv(storage));
281939
+ if (!(0, import_fs12.existsSync)(envFile)) {
281940
+ (0, import_fs12.writeFileSync)(envFile, generateEnv(storage));
281901
281941
  created.push(envFile);
281902
281942
  } else {
281903
- const existing = (0, import_fs11.readFileSync)(envFile, "utf-8");
281943
+ const existing = (0, import_fs12.readFileSync)(envFile, "utf-8");
281904
281944
  if (!existing.includes("ABLO_")) {
281905
- (0, import_fs11.writeFileSync)(envFile, existing + "\n" + generateEnv(storage));
281906
- 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)")}`);
281907
281947
  } else {
281908
- created.push(`${envFile} ${import_picocolors16.default.dim("(already configured)")}`);
281948
+ created.push(`${envFile} ${import_picocolors17.default.dim("(already configured)")}`);
281909
281949
  }
281910
281950
  }
281911
281951
  if (agent) {
281912
- (0, import_fs11.writeFileSync)((0, import_path7.join)(abloDir, "agent.ts"), generateAgent());
281952
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "agent.ts"), generateAgent());
281913
281953
  created.push(`${abloDir}/agent.ts`);
281914
281954
  }
281915
281955
  if (framework === "nextjs") {
281916
281956
  if (storage === "endpoint") {
281917
281957
  const webhookDir = (0, import_path7.join)("app", "api", "ablo", "webhooks");
281918
- (0, import_fs11.mkdirSync)(webhookDir, { recursive: true });
281919
- (0, import_fs11.writeFileSync)((0, import_path7.join)(webhookDir, "route.ts"), generateWebhookRoute(orm));
281958
+ (0, import_fs12.mkdirSync)(webhookDir, { recursive: true });
281959
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(webhookDir, "route.ts"), generateWebhookRoute(orm));
281920
281960
  created.push(`${webhookDir}/route.ts${orm === "prisma" ? " (Prisma mirror)" : " (add your database write)"}`);
281921
281961
  }
281922
- (0, import_fs11.writeFileSync)((0, import_path7.join)("app", "providers.tsx"), generateProviders());
281923
- created.push(`app/providers.tsx ${import_picocolors16.default.dim("(wrap app/layout.tsx in <Providers>)")}`);
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>)")}`);
281924
281964
  const sessionDir = (0, import_path7.join)("app", "api", "ablo-session");
281925
- (0, import_fs11.mkdirSync)(sessionDir, { recursive: true });
281926
- (0, import_fs11.writeFileSync)((0, import_path7.join)(sessionDir, "route.ts"), generateSessionRoute());
281927
- created.push(`app/api/ablo-session/route.ts ${import_picocolors16.default.dim("(wire your auth)")}`);
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)")}`);
281928
281968
  }
281929
281969
  if (framework !== "vanilla") {
281930
- (0, import_fs11.writeFileSync)((0, import_path7.join)(abloDir, "TaskList.tsx"), generateComponent());
281970
+ (0, import_fs12.writeFileSync)((0, import_path7.join)(abloDir, "TaskList.tsx"), generateComponent());
281931
281971
  created.push(`${abloDir}/TaskList.tsx`);
281932
281972
  }
281933
- 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");
281934
281974
  const pm = detectPackageManager();
281935
281975
  if (opts.install) {
281936
281976
  const s = Y2();
@@ -281939,38 +281979,43 @@ async function init(args = []) {
281939
281979
  (0, import_child_process2.execSync)(`${pm} add @abloatai/ablo`, { stdio: "ignore" });
281940
281980
  s.stop("Installed @abloatai/ablo");
281941
281981
  } catch {
281942
- 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`)}`);
281943
281983
  }
281944
281984
  }
281945
281985
  const steps = [
281946
- `Get a ${import_picocolors16.default.bold("sk_test_")} key at ${import_picocolors16.default.cyan("https://abloatai.com")}`,
281947
- `Run ${import_picocolors16.default.bold("npx ablo login")} (or add ${import_picocolors16.default.bold("ABLO_API_KEY")} to ${import_picocolors16.default.bold(envFile)})`,
281948
- `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`,
281949
- `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`,
281950
281990
  ...storage === "direct" ? [
281951
- `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)`
281952
281992
  ] : [
281953
- `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")}`
281954
281994
  ],
281955
281995
  ...framework === "nextjs" ? [
281956
- `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")}`
281957
281997
  ] : [],
281958
- `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`,
281959
281999
  ...agent ? [
281960
- `Run ${import_picocolors16.default.bold(`npx tsx ${abloDir}/agent.ts`)} \u2014 an AI teammate edits the same tasks`,
281961
- `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`
281962
282002
  ] : []
281963
282003
  ];
281964
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
+ }
281965
282010
  if (interactive && opts.login) {
281966
282011
  const loginNow = await ye({ message: "Log in now? (opens your browser)", initialValue: true });
281967
282012
  if (!pD(loginNow) && loginNow) {
281968
- Se(`${import_picocolors16.default.dim("Docs:")} https://abloatai.com/docs`);
282013
+ Se(`${import_picocolors17.default.dim("Docs:")} https://abloatai.com/docs`);
281969
282014
  await login();
281970
282015
  return;
281971
282016
  }
281972
282017
  }
281973
- 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`);
281974
282019
  }
281975
282020
  function generateSchema() {
281976
282021
  return `import { defineSchema, model, relation, z } from '@abloatai/ablo/schema';
@@ -282324,9 +282369,9 @@ async function getCurrentUser(): Promise<{ id: string } | null> {
282324
282369
  `;
282325
282370
  }
282326
282371
  function detectPackageManager() {
282327
- if ((0, import_fs11.existsSync)("pnpm-lock.yaml")) return "pnpm";
282328
- if ((0, import_fs11.existsSync)("yarn.lock")) return "yarn";
282329
- 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";
282330
282375
  return "npm";
282331
282376
  }
282332
282377
  main().catch(console.error);