agentlink-sh 0.30.1 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +70 -45
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -1123,6 +1123,7 @@ function formatBytes(n) {
1123
1123
  import fs4 from "fs";
1124
1124
  import path4 from "path";
1125
1125
  import { input as input2, select } from "@inquirer/prompts";
1126
+ import open from "open";
1126
1127
  var promptTheme = {
1127
1128
  prefix: { idle: blue("?"), done: blue("\u2714") },
1128
1129
  style: {
@@ -1447,7 +1448,30 @@ async function resendSetup(cwd, opts = {}) {
1447
1448
  apiKey = globalDefaults.api_key;
1448
1449
  fromEmail = globalDefaults.from_email;
1449
1450
  } else {
1450
- console.log(` ${dim("Get your API key at:")} ${blue("https://resend.com/api-keys")}`);
1451
+ const apiKeysUrl = "https://resend.com/api-keys";
1452
+ if (!hasExistingApiKey) {
1453
+ const hasKeyAtHand = await selectYesNo({
1454
+ message: "Do you have a Resend API key at hand?",
1455
+ default: true,
1456
+ yesLabel: "Yes \u2014 I'll paste it in the next step",
1457
+ noLabel: "No, I need to create one \u2014 open the Resend dashboard",
1458
+ theme: promptTheme
1459
+ });
1460
+ console.log();
1461
+ if (hasKeyAtHand) {
1462
+ console.log(` ${dim("Get your API key at:")} ${blue(apiKeysUrl)}`);
1463
+ } else {
1464
+ try {
1465
+ await open(apiKeysUrl);
1466
+ console.log(` ${blue("\u25CF")} Opened ${blue(apiKeysUrl)}`);
1467
+ } catch {
1468
+ console.log(` ${amber("Could not open browser.")} Visit: ${blue(apiKeysUrl)}`);
1469
+ }
1470
+ console.log(` ${dim("Create a key in the dashboard, then paste it below.")}`);
1471
+ }
1472
+ } else {
1473
+ console.log(` ${dim("Get your API key at:")} ${blue(apiKeysUrl)}`);
1474
+ }
1451
1475
  const rawKey = (await input2({
1452
1476
  message: hasExistingApiKey ? "Resend API key (press Enter to keep current):" : "Resend API key:",
1453
1477
  theme: apiKeyTheme,
@@ -5455,6 +5479,37 @@ DROP TRIGGER IF EXISTS trg_auth_users_new_user ON auth.users;
5455
5479
  CREATE TRIGGER trg_auth_users_new_user
5456
5480
  AFTER INSERT ON auth.users
5457
5481
  FOR EACH ROW EXECUTE FUNCTION public._internal_admin_handle_new_user();
5482
+
5483
+ -- supabase_auth_admin (the GoTrue auth service role) needs USAGE on the
5484
+ -- public schema to execute the _hook_* functions it calls during signup and
5485
+ -- email flows. Supabase pre-grants public USAGE to postgres/anon/
5486
+ -- authenticated/service_role but NOT to supabase_auth_admin, and the regen
5487
+ -- script's platform-default strip drops it from pg-delta's plan \u2014 so
5488
+ -- re-assert it here. Without it a migration-built DB diverges from a
5489
+ -- db-apply-built one. Idempotent.
5490
+ GRANT USAGE ON SCHEMA public TO supabase_auth_admin;
5491
+
5492
+ -- Lock down the api._admin_* SECURITY DEFINER functions to service_role.
5493
+ --
5494
+ -- These bypass RLS and have no auth_verify_access guard \u2014 they're meant to
5495
+ -- be called only by trusted server-side code (edge functions via the
5496
+ -- service role). They live in the api schema, which IS exposed via
5497
+ -- PostgREST, so a stray client grant is directly reachable.
5498
+ --
5499
+ -- WHY THIS IS HERE AND NOT IN THE pg-delta PLAN: Postgres grants EXECUTE to
5500
+ -- PUBLIC by default on every new function. The declarative schemas
5501
+ -- (_admin_queues.sql) revoke that with an explicit
5502
+ -- "REVOKE ALL ... FROM PUBLIC, anon, authenticated", but pg-delta's plan
5503
+ -- does NOT model the built-in PUBLIC default grant \u2014 it only emits
5504
+ -- "REVOKE ... FROM authenticated" and leaves the implicit PUBLIC grant in
5505
+ -- place. anon/authenticated are members of PUBLIC, so without this block a
5506
+ -- migration-built DB (fresh prod via db push) would leave these privileged
5507
+ -- functions anon-callable, diverging from a db-apply-built dev DB. Re-assert
5508
+ -- the full revoke here so both paths converge. REVOKE is idempotent.
5509
+ REVOKE ALL ON FUNCTION api._admin_enqueue_task(text, jsonb, integer) FROM PUBLIC, anon, authenticated;
5510
+ REVOKE ALL ON FUNCTION api._admin_queue_read(integer, integer) FROM PUBLIC, anon, authenticated;
5511
+ REVOKE ALL ON FUNCTION api._admin_queue_delete(bigint) FROM PUBLIC, anon, authenticated;
5512
+ REVOKE ALL ON FUNCTION api._admin_queue_archive(bigint) FROM PUBLIC, anon, authenticated;
5458
5513
  `;
5459
5514
 
5460
5515
  // src/template/supabase/functions/_shared/responses.ts
@@ -11008,7 +11063,7 @@ import fs18 from "fs";
11008
11063
  import path18 from "path";
11009
11064
  import { fileURLToPath as fileURLToPath3 } from "url";
11010
11065
  import { input as input6, select as select5 } from "@inquirer/prompts";
11011
- import open from "open";
11066
+ import open2 from "open";
11012
11067
 
11013
11068
  // src/banner.ts
11014
11069
  var TITLE = [
@@ -11237,40 +11292,6 @@ function ensureClaudeSettings(projectDir, isCloud) {
11237
11292
  fs14.writeFileSync(filePath, JSON.stringify(merged, null, 2) + "\n");
11238
11293
  }
11239
11294
 
11240
- // src/migrate.ts
11241
- var BASELINE_MIGRATIONS = [
11242
- {
11243
- version: "20200101000000",
11244
- name: "initial_agentlink_bootstrap"
11245
- },
11246
- {
11247
- version: "20200101000001",
11248
- name: "initial_agentlink_scaffold"
11249
- }
11250
- ];
11251
- var BASELINE_MIGRATION = BASELINE_MIGRATIONS[1];
11252
- var MARK_BASELINE_MIGRATIONS_APPLIED_SQL = `
11253
- -- Ensure the schema_migrations ledger exists. On a freshly-created
11254
- -- Supabase project this is usually pre-created by the platform, but
11255
- -- we don't depend on that \u2014 IF NOT EXISTS makes both lines safe to
11256
- -- re-run.
11257
- CREATE SCHEMA IF NOT EXISTS supabase_migrations;
11258
-
11259
- CREATE TABLE IF NOT EXISTS supabase_migrations.schema_migrations (
11260
- version text PRIMARY KEY,
11261
- statements text[],
11262
- name text
11263
- );
11264
-
11265
- -- Mark the agentlink baselines as applied. The actual SQL has already
11266
- -- been run via pg-delta declarative apply (in scaffold) or will be run
11267
- -- via \`supabase db push\` (in env add prod). These rows only exist to
11268
- -- tell future \`db push\` calls "you don't need to apply these again."
11269
- INSERT INTO supabase_migrations.schema_migrations (version, name) VALUES
11270
- ${BASELINE_MIGRATIONS.map((m) => ` ('${m.version}', '${m.name}')`).join(",\n")}
11271
- ON CONFLICT (version) DO NOTHING;
11272
- `;
11273
-
11274
11295
  // src/scaffold.ts
11275
11296
  import { fileURLToPath as fileURLToPath2 } from "url";
11276
11297
  var __dirname = path15.dirname(fileURLToPath2(import.meta.url));
@@ -11608,8 +11629,6 @@ SUPABASE_DB_PASSWORD=${ctx.dbPass}
11608
11629
  }
11609
11630
  spinner.text = "Setting up database \u2014 Waiting for Postgres to accept connections";
11610
11631
  await waitForDatabaseReady(ctx.projectRef, spinner);
11611
- spinner.text = "Setting up database \u2014 Applying schemas (extensions, isolation, auth hooks, triggers)";
11612
- await dbApply({ cwd: projectDir, dbUrl: ctx.dbUrl, skipTypes: true, quiet: true });
11613
11632
  for (const [relPath, content] of Object.entries(TEMPLATE_FILES)) {
11614
11633
  if (!relPath.startsWith("migrations/")) continue;
11615
11634
  const full = path15.join(projectDir, "supabase", relPath);
@@ -11618,8 +11637,13 @@ SUPABASE_DB_PASSWORD=${ctx.dbPass}
11618
11637
  fs15.writeFileSync(full, content);
11619
11638
  }
11620
11639
  }
11621
- spinner.text = "Setting up database \u2014 Marking baseline migrations applied";
11622
- await runCloudSQL(MARK_BASELINE_MIGRATIONS_APPLIED_SQL, ctx.projectRef);
11640
+ spinner.text = "Setting up database \u2014 Applying baseline migrations";
11641
+ await runCommand(
11642
+ `${sb()} db push --db-url ${JSON.stringify(ctx.dbUrl)}`,
11643
+ projectDir,
11644
+ void 0,
11645
+ cloudEnv
11646
+ );
11623
11647
  spinner.text = "Setting up database \u2014 Storing API keys in Vault";
11624
11648
  await runCloudSQL(cloudSeedSQL(ctx.apiUrl, ctx.publishableKey, ctx.secretKey), ctx.projectRef);
11625
11649
  spinner.text = "Setting up database \u2014 Deploying edge functions (internal-send-auth-email, internal-queue-worker, internal-invite-member)";
@@ -11634,8 +11658,6 @@ SUPABASE_DB_PASSWORD=${ctx.dbPass}
11634
11658
  spinner.text = "Setting up database \u2014 Enabling auth hooks and signup settings";
11635
11659
  await updateAuthConfig(ctx.projectRef, { ...AUTH_CONFIG, ...AUTH_CONFIG_DEV_OVERRIDES });
11636
11660
  } else {
11637
- spinner.text = "Setting up database \u2014 Applying schemas (extensions, isolation, auth hooks, triggers)";
11638
- await dbApply({ cwd: projectDir, dbUrl: ctx.dbUrl, skipTypes: true, quiet: true });
11639
11661
  for (const [relPath, content] of Object.entries(TEMPLATE_FILES)) {
11640
11662
  if (!relPath.startsWith("migrations/")) continue;
11641
11663
  const full = path15.join(projectDir, "supabase", relPath);
@@ -11644,8 +11666,11 @@ SUPABASE_DB_PASSWORD=${ctx.dbPass}
11644
11666
  fs15.writeFileSync(full, content);
11645
11667
  }
11646
11668
  }
11647
- spinner.text = "Setting up database \u2014 Marking baseline migrations applied";
11648
- await runSQL(MARK_BASELINE_MIGRATIONS_APPLIED_SQL, ctx.dbUrl);
11669
+ spinner.text = "Setting up database \u2014 Applying baseline migrations";
11670
+ await runCommand(
11671
+ `${sb()} db push --db-url ${JSON.stringify(ctx.dbUrl)}`,
11672
+ projectDir
11673
+ );
11649
11674
  spinner.text = "Setting up database \u2014 Storing API keys in Vault";
11650
11675
  await runSQL(seedSQL(ctx.publishableKey, ctx.secretKey), ctx.dbUrl);
11651
11676
  }
@@ -12565,7 +12590,7 @@ async function runFinalActionPicker(summary, relPath) {
12565
12590
  if (action === "exit") return;
12566
12591
  if (action === "dashboard") {
12567
12592
  try {
12568
- await open(dashboardUrl);
12593
+ await open2(dashboardUrl);
12569
12594
  console.log();
12570
12595
  console.log(` ${dim("Opened")} ${blue(dashboardUrl)}`);
12571
12596
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentlink-sh",
3
- "version": "0.30.1",
3
+ "version": "0.31.0",
4
4
  "description": "CLI for building Supabase apps with AI agents",
5
5
  "bin": {
6
6
  "agentlink": "dist/index.js"
@@ -13,7 +13,8 @@
13
13
  "build": "tsup",
14
14
  "dev": "tsup --watch",
15
15
  "lint": "eslint src/",
16
- "regen-migrations": "tsx scripts/regen-baseline-migration.ts"
16
+ "regen-migrations": "tsx scripts/regen-baseline-migration.ts",
17
+ "check-migrations": "tsx scripts/check-migrations.ts"
17
18
  },
18
19
  "dependencies": {
19
20
  "@inquirer/prompts": "^8.3.0",