agentlink-sh 0.30.0 → 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.
- package/dist/index.js +71 -45
- 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
|
-
|
|
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
|
|
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
|
|
11622
|
-
await
|
|
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
|
|
11648
|
-
await
|
|
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
|
|
12593
|
+
await open2(dashboardUrl);
|
|
12569
12594
|
console.log();
|
|
12570
12595
|
console.log(` ${dim("Opened")} ${blue(dashboardUrl)}`);
|
|
12571
12596
|
console.log();
|
|
@@ -12914,6 +12939,7 @@ ${red("Error:")} ${error}`);
|
|
|
12914
12939
|
}
|
|
12915
12940
|
let cloudConfig;
|
|
12916
12941
|
if (useCloud && !skipEnv) {
|
|
12942
|
+
await ensureAccessToken({ nonInteractive, projectDir: process.cwd() });
|
|
12917
12943
|
const picked = await pickOrg(process.cwd(), {
|
|
12918
12944
|
org: opts.orgId,
|
|
12919
12945
|
nonInteractive
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlink-sh",
|
|
3
|
-
"version": "0.
|
|
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",
|