@supacloud/cli 0.113.0 → 0.115.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 CHANGED
@@ -8901,9 +8901,11 @@ function parseCliFlags(args, startIndex, schema) {
8901
8901
  if (!flag)
8902
8902
  continue;
8903
8903
  index = flag.nextIndex;
8904
- if (Object.hasOwn(parsedFlags, flag.name))
8904
+ const normalizedName = flag.name.replaceAll("-", "_");
8905
+ const schemaName = Object.hasOwn(fields, flag.name) ? flag.name : Object.hasOwn(fields, normalizedName) ? normalizedName : flag.name;
8906
+ if (Object.hasOwn(parsedFlags, schemaName))
8905
8907
  throw new Error(DUPLICATE_CLI_FLAG_MESSAGE);
8906
- parsedFlags[flag.name] = flag.rawValue === undefined ? true : schemaCompatibleCliValue(fields[flag.name], flag.rawValue);
8908
+ parsedFlags[schemaName] = flag.rawValue === undefined ? true : schemaCompatibleCliValue(fields[schemaName], flag.rawValue);
8907
8909
  }
8908
8910
  return parsedFlags;
8909
8911
  }
@@ -8918,18 +8920,19 @@ async function runCli(cliTools, args, options = {}) {
8918
8920
  const shape = getSchemaShape(tool.schema);
8919
8921
  const actionField = shape.action;
8920
8922
  const actionOptions = getEnumOptions(actionField);
8921
- const actionless = toolName === "deploy";
8923
+ const actionless = actionOptions.length === 0;
8922
8924
  const otherFields = Object.entries(shape).filter(([name]) => name !== "action");
8923
8925
  const actionLines = actionOptions.length ? `Available actions:
8924
8926
  ${actionOptions.join(`
8925
8927
  `)}` : actionless ? "" : "This command does not declare action metadata.";
8928
+ const actionUsage = actionOptions.length ? toolName === "deploy" ? " [action]" : " <action>" : "";
8926
8929
  const argLines = otherFields.length ? otherFields.map(([name, field]) => {
8927
8930
  const description = getDescription(field) || "(no description)";
8928
8931
  return ` --${name} ${description}`;
8929
8932
  }).join(`
8930
8933
  `) : " (no additional flags)";
8931
8934
  return [
8932
- `Usage: ${commandName} ${toolName}${actionless ? "" : " <action>"} [--flags]`,
8935
+ `Usage: ${commandName} ${toolName}${actionUsage} [--flags]`,
8933
8936
  "",
8934
8937
  ...actionLines ? [actionLines, ""] : [],
8935
8938
  "Flags:",
@@ -9028,11 +9031,8 @@ var CORE_CONTEXT_KEYS = [
9028
9031
  "SUPABASE_URL",
9029
9032
  "SUPABASE_SERVICE_ROLE_KEY",
9030
9033
  "SUPACLOUD_API_URL",
9031
- "SUPACLOUD_MANAGEMENT_API_URL",
9032
- "MANAGEMENT_API_URL",
9033
9034
  "SUPACLOUD_API_TOKEN",
9034
9035
  "SUPACLOUD_PROJECT_REF",
9035
- "X_PROJECT_REF",
9036
9036
  "SUPACLOUD_HOST",
9037
9037
  "SUPACLOUD_APPLICATION_ID",
9038
9038
  "SUPACLOUD_APPLICATION_ENVIRONMENT_ID",
@@ -9103,8 +9103,8 @@ function sourceCredentialScope(values, explicitApiUrl, supabaseUrl) {
9103
9103
  }
9104
9104
  function sourceProjectCore(values) {
9105
9105
  const supabaseUrl = canonicalApiOrigin(values.SUPABASE_URL || "");
9106
- const projectRef = (values.SUPACLOUD_PROJECT_REF || values.X_PROJECT_REF || "").trim() || inferProjectRefFromSupabaseUrl(supabaseUrl);
9107
- const explicitApiUrl = values.SUPACLOUD_API_URL || values.SUPACLOUD_MANAGEMENT_API_URL || values.MANAGEMENT_API_URL || "";
9106
+ const projectRef = (values.SUPACLOUD_PROJECT_REF || "").trim() || inferProjectRefFromSupabaseUrl(supabaseUrl);
9107
+ const explicitApiUrl = values.SUPACLOUD_API_URL || "";
9108
9108
  const credentialScope = sourceCredentialScope(values, explicitApiUrl, supabaseUrl);
9109
9109
  const managementUrl = explicitApiUrl || (values.SUPACLOUD_HOST ? `http://${values.SUPACLOUD_HOST}:9090` : "");
9110
9110
  const apiUrl = credentialScope === "management" ? canonicalApiOrigin(managementUrl) : "";
@@ -9125,17 +9125,14 @@ function completeProjectContext(values) {
9125
9125
  return Boolean(core.apiUrl && core.apiToken && core.projectRef);
9126
9126
  }
9127
9127
  function parseInsecureTls(values) {
9128
- for (const key of ["SUPACLOUD_TLS_VERIFY", "SUPACLOUD_TLS_INSECURE"]) {
9129
- const value = values[key]?.trim().toLowerCase();
9130
- if (value === undefined)
9131
- continue;
9132
- if (["1", "true", "yes", "on"].includes(value))
9133
- return key === "SUPACLOUD_TLS_INSECURE";
9134
- if (["0", "false", "no", "off"].includes(value))
9135
- return key === "SUPACLOUD_TLS_VERIFY";
9136
- throw new Error(`${key} must be a boolean (true or false)`);
9137
- }
9138
- return true;
9128
+ const value = values.SUPACLOUD_TLS_VERIFY?.trim().toLowerCase();
9129
+ if (value === undefined)
9130
+ return true;
9131
+ if (["1", "true", "yes", "on"].includes(value))
9132
+ return false;
9133
+ if (["0", "false", "no", "off"].includes(value))
9134
+ return true;
9135
+ throw new Error("SUPACLOUD_TLS_VERIFY must be a boolean (true or false)");
9139
9136
  }
9140
9137
  function namedEnvironmentSource(cwd, selector) {
9141
9138
  const environment = normalizeEnvironmentName(selector);
@@ -9270,7 +9267,7 @@ function parseGlobalOptions(args) {
9270
9267
 
9271
9268
  // src/shared/execution-policy.ts
9272
9269
  var ACTION_POLICY = {
9273
- deploy: { write: ["deploy"] },
9270
+ deploy: { local: ["init"], write: ["deploy"] },
9274
9271
  applications: {
9275
9272
  read: ["list_releases", "get_release", "get_runtime", "get_deployment_evidence", "get_configuration", "get_release_transfer_plan", "get_promotion_plan", "get_promotion_status", "get_rollback_snapshot", "get_history", "get_deploy_plan", "logs", "get_preview_plan", "list_previews", "get_preview"],
9276
9273
  write: ["upload_release", "put_configuration", "deploy_application", "transfer_release", "promote_application", "reconcile_promotion", "create_preview", "reconcile_preview", "cleanup_preview", "activate_release", "deploy_release", "rollback_release", "reconcile_activation", "retire_activation"]
@@ -9281,7 +9278,7 @@ var ACTION_POLICY = {
9281
9278
  local: ["list"]
9282
9279
  },
9283
9280
  database: {
9284
- read: ["list_tables", "describe_columns", "list_indexes", "list_constraints", "list_extensions", "extension_catalog", "rls_status", "rls_policies", "list_auth_users", "get_auth_user", "connections", "stats", "slow_queries", "list_migrations", "migration_inventory", "migration_import", "delivery_migration_plan", "project_url", "generate_types", "database_lint", "db_lint", "rpc_catalog", "list_rpcs"],
9281
+ read: ["list_tables", "describe_columns", "export_svadmin_schema", "list_indexes", "list_constraints", "list_extensions", "extension_catalog", "rls_status", "rls_policies", "list_auth_users", "get_auth_user", "connections", "stats", "slow_queries", "list_migrations", "migration_inventory", "migration_import", "delivery_migration_plan", "project_url", "generate_types", "database_lint", "db_lint", "rpc_catalog", "list_rpcs", "governance_check", "role_check"],
9285
9282
  local: ["lint_migrations", "lint", "rebase_migrations"],
9286
9283
  write: ["query", "execute", "apply_migration", "push_migrations", "baseline_migrations", "create_table_rls", "enable_extension", "disable_extension"]
9287
9284
  },
@@ -12909,6 +12906,7 @@ function registerDatabaseTools(server, http, config = {}) {
12909
12906
  "query",
12910
12907
  "list_tables",
12911
12908
  "describe_columns",
12909
+ "export_svadmin_schema",
12912
12910
  "list_indexes",
12913
12911
  "list_constraints",
12914
12912
  "list_extensions",
@@ -12929,7 +12927,9 @@ function registerDatabaseTools(server, http, config = {}) {
12929
12927
  "db_lint",
12930
12928
  "rpc_catalog",
12931
12929
  "list_rpcs",
12932
- "migration_import"
12930
+ "migration_import",
12931
+ "governance_check",
12932
+ "role_check"
12933
12933
  ];
12934
12934
  const writeActions = ["execute", "apply_migration", "push_migrations", "baseline_migrations", "create_table_rls", "enable_extension", "disable_extension"];
12935
12935
  const remoteActions = [...readActions, ...localActions];
@@ -12947,10 +12947,11 @@ Actions: ${allActions.join(", ")}${localOnly ? " (local-only mode)" : readOnly ?
12947
12947
  strict: optional(Boolean2(), "[push_migrations/lint_migrations/lint/database_lint] Exit with error if high-risk issues or migrations are detected"),
12948
12948
  fail_on_high: optional(Boolean2(), "[push_migrations/lint_migrations/lint/database_lint] Alias for --strict"),
12949
12949
  fail_on_medium: optional(Boolean2(), "[push_migrations/lint_migrations/lint] Exit with error if medium-risk or high-risk migrations are detected"),
12950
- json: optional(Boolean2(), "[lint_migrations/lint/database_lint/rpc_catalog] Output structured JSON analysis"),
12950
+ format: optional(stringEnum2(["text", "json"]), "[lint_migrations/lint/database_lint/rpc_catalog] Output format (default: text)"),
12951
12951
  schema: optional(String2(), "[describe_columns/list_indexes/list_constraints/rls_status/rls_policies/create_table_rls/database_lint] Schema name (default: public)"),
12952
12952
  table: optional(String2(), "[describe_columns/indexes/constraints/rls_*] Table name"),
12953
- schemas: optional(_Array_(String2()), "[list_tables/generate_types/rpc_catalog] Schemas array"),
12953
+ schemas: optional(_Array_(String2()), "[list_tables/generate_types/rpc_catalog/governance_check/role_check] Schemas array"),
12954
+ modules: optional(_Array_(Record(String2(), Unknown()), { maxItems: 64 }), "[governance_check] JSON-safe database module declarations"),
12954
12955
  user_id: optional(String2(), "[get_auth_user] User UUID"),
12955
12956
  limit: optional(Number2(), "[list_auth_users] Max users (default: 20)"),
12956
12957
  name: optional(String2(), "[apply_migration] Migration name"),
@@ -13105,6 +13106,38 @@ Actions: ${allActions.join(", ")}${localOnly ? " (local-only mode)" : readOnly ?
13105
13106
  text = r.ok ? formatColumnsList(r.data, schema, args.table) : `❌ Failed (${r.status})`;
13106
13107
  break;
13107
13108
  }
13109
+ case "export_svadmin_schema": {
13110
+ if (!args.table)
13111
+ throw new Error("'table' required");
13112
+ const schemaName = validatedIdentifier(schema, "schema");
13113
+ const tableName = validatedIdentifier(args.table, "table");
13114
+ const schemaLiteral = `'${schemaName}'`;
13115
+ const tableLiteral = `'${tableName}'`;
13116
+ const regclassLiteral = `'${schemaName}.${tableName}'`;
13117
+ const colSql = `SELECT column_name, data_type, is_nullable, column_default, character_maximum_length FROM information_schema.columns WHERE table_schema = ${schemaLiteral} AND table_name = ${tableLiteral} ORDER BY ordinal_position;`;
13118
+ const colRes = await execSql(colSql);
13119
+ if (!colRes.ok)
13120
+ throw new Error(`Failed to inspect columns (${colRes.status})`);
13121
+ const conSql = `SELECT conname as name, contype as type, pg_get_constraintdef(oid) as definition FROM pg_constraint WHERE connamespace = ${schemaLiteral}::regnamespace AND conrelid = ${regclassLiteral}::regclass;`;
13122
+ const conRes = await execSql(conSql);
13123
+ if (!conRes.ok)
13124
+ throw new Error(`Failed to inspect constraints (${conRes.status})`);
13125
+ const cols = buildSvadminColumns(colRes.data, conRes.data);
13126
+ if (args.format === "json") {
13127
+ return {
13128
+ content: [{
13129
+ type: "text",
13130
+ text: JSON.stringify({
13131
+ resource: args.table,
13132
+ sourceType: "databaseColumns",
13133
+ columns: cols
13134
+ }, null, 2)
13135
+ }]
13136
+ };
13137
+ }
13138
+ text = formatSvadminSchemaExport(args.table, cols);
13139
+ break;
13140
+ }
13108
13141
  case "list_indexes": {
13109
13142
  if (!args.table)
13110
13143
  throw new Error("'table' required");
@@ -13266,7 +13299,7 @@ Actions: ${allActions.join(", ")}${localOnly ? " (local-only mode)" : readOnly ?
13266
13299
  migrationFiles = sortMigrationFiles(files.map((file) => readMigrationFile(dir, file)));
13267
13300
  }
13268
13301
  const analysis = analyzeMigrationFiles(migrationFiles);
13269
- if (args.json) {
13302
+ if (args.format === "json") {
13270
13303
  text = JSON.stringify(analysis, null, 2);
13271
13304
  } else {
13272
13305
  text = formatMigrationRiskReport(analysis);
@@ -13501,7 +13534,7 @@ ${riskReport}`
13501
13534
  if (!r.ok) {
13502
13535
  return { content: [{ type: "text", text: `❌ Database lint request failed (${r.status})` }], isError: true };
13503
13536
  }
13504
- if (args.json) {
13537
+ if (args.format === "json") {
13505
13538
  text = JSON.stringify(r.data, null, 2);
13506
13539
  } else {
13507
13540
  text = formatDatabaseLintReport(r.data);
@@ -13516,6 +13549,45 @@ ${riskReport}`
13516
13549
  }
13517
13550
  break;
13518
13551
  }
13552
+ case "governance_check": {
13553
+ const schemasList = args.schemas && args.schemas.length > 0 ? args.schemas : args.schema ? [args.schema] : ["public"];
13554
+ const safeRef = projectRefPathSegment(ref, "governance_check");
13555
+ const r = await managementHttp().post(`/v1/projects/${safeRef}/database/module-check`, {
13556
+ modules: args.modules,
13557
+ schemas: schemasList
13558
+ });
13559
+ if (!r.ok) {
13560
+ return { content: [{ type: "text", text: `❌ Database module check failed (${r.status})` }], isError: true };
13561
+ }
13562
+ const result = recordPayload(r.data);
13563
+ if (!result || typeof result["ok"] !== "boolean" || !Array.isArray(result["reports"])) {
13564
+ throw new Error("Invalid database module check response");
13565
+ }
13566
+ text = formatModuleCheckReport(r.data);
13567
+ if (!result["ok"]) {
13568
+ return { content: [{ type: "text", text }], isError: true };
13569
+ }
13570
+ break;
13571
+ }
13572
+ case "role_check": {
13573
+ const schemasList = args.schemas && args.schemas.length > 0 ? args.schemas : args.schema ? [args.schema] : ["public"];
13574
+ const safeRef = projectRefPathSegment(ref, "role_check");
13575
+ const r = await managementHttp().post(`/v1/projects/${safeRef}/database/role-check`, {
13576
+ schemas: schemasList
13577
+ });
13578
+ if (!r.ok) {
13579
+ return { content: [{ type: "text", text: `❌ Database role check failed (${r.status})` }], isError: true };
13580
+ }
13581
+ const result = recordPayload(r.data);
13582
+ if (!result || typeof result["ok"] !== "boolean" || !recordPayload(result["state"]) || result["error"] !== undefined && typeof result["error"] !== "string") {
13583
+ throw new Error("Invalid database role check response");
13584
+ }
13585
+ text = JSON.stringify(r.data, null, 2);
13586
+ if (!result["ok"]) {
13587
+ return { content: [{ type: "text", text }], isError: true };
13588
+ }
13589
+ break;
13590
+ }
13519
13591
  case "rpc_catalog":
13520
13592
  case "list_rpcs": {
13521
13593
  const schemasList = args.schemas && args.schemas.length > 0 ? args.schemas : args.schema ? [args.schema] : ["public", "api"];
@@ -13524,7 +13596,7 @@ ${riskReport}`
13524
13596
  if (!r.ok) {
13525
13597
  return { content: [{ type: "text", text: `❌ RPC catalog request failed (${r.status})` }], isError: true };
13526
13598
  }
13527
- if (args.json) {
13599
+ if (args.format === "json") {
13528
13600
  text = JSON.stringify(r.data, null, 2);
13529
13601
  } else {
13530
13602
  text = formatRpcCatalog(r.data);
@@ -13538,10 +13610,13 @@ ${riskReport}`
13538
13610
  });
13539
13611
  }
13540
13612
  function quoteIdentifier(value, label) {
13613
+ return `"${validatedIdentifier(value, label)}"`;
13614
+ }
13615
+ function validatedIdentifier(value, label) {
13541
13616
  if (typeof value !== "string" || !/^[A-Za-z_][A-Za-z0-9_]{0,62}$/.test(value)) {
13542
13617
  throw new Error(`Invalid ${label} identifier`);
13543
13618
  }
13544
- return `"${value}"`;
13619
+ return value;
13545
13620
  }
13546
13621
  function validateColumnDefinitions(value) {
13547
13622
  if (typeof value !== "string")
@@ -13597,6 +13672,34 @@ function formatDatabaseLintReport(data) {
13597
13672
  return lines.join(`
13598
13673
  `).trimEnd();
13599
13674
  }
13675
+ function formatModuleCheckReport(data) {
13676
+ const result = recordPayload(data);
13677
+ const reports = Array.isArray(result?.["reports"]) ? result["reports"] : [];
13678
+ const lines = [];
13679
+ for (const raw of reports) {
13680
+ const report = recordPayload(raw);
13681
+ if (!report)
13682
+ continue;
13683
+ const name = typeof report["module"] === "string" ? report["module"] : "unknown";
13684
+ const ok = report["ok"] === true;
13685
+ lines.push(`module: ${name} (${ok ? "ok" : "FAILED"})`);
13686
+ const issues = Array.isArray(report["issues"]) ? report["issues"] : [];
13687
+ if (issues.length === 0)
13688
+ lines.push(" no issues");
13689
+ for (const rawIssue of issues) {
13690
+ const issue = recordPayload(rawIssue);
13691
+ if (!issue)
13692
+ continue;
13693
+ const severity = typeof issue["severity"] === "string" ? issue["severity"] : "error";
13694
+ const code = typeof issue["code"] === "string" ? issue["code"] : "unknown";
13695
+ const object = typeof issue["object"] === "string" ? issue["object"] : "?";
13696
+ const message = typeof issue["message"] === "string" ? issue["message"] : "";
13697
+ lines.push(` ${severity} ${code} ${object} ${message}`);
13698
+ }
13699
+ }
13700
+ return lines.length > 0 ? lines.join(`
13701
+ `) : JSON.stringify(data, null, 2);
13702
+ }
13600
13703
  function formatRpcCatalog(data) {
13601
13704
  if (!data || typeof data !== "object")
13602
13705
  return JSON.stringify(data, null, 2);
@@ -13713,6 +13816,67 @@ function formatConstraintList(data) {
13713
13816
  `).join(`
13714
13817
  `);
13715
13818
  }
13819
+ function buildSvadminColumns(colData, conData) {
13820
+ const colRows = responseRows(colData, _Object_({
13821
+ column_name: String2(),
13822
+ data_type: String2(),
13823
+ is_nullable: String2(),
13824
+ character_maximum_length: Optional(Union([Number2(), Null()])),
13825
+ column_default: Optional(Union([String2(), Null()]))
13826
+ }));
13827
+ const conRows = responseRows(conData, _Object_({
13828
+ name: String2(),
13829
+ type: String2(),
13830
+ definition: String2()
13831
+ }));
13832
+ const pkConstraint = conRows.find((c) => c.type === "p");
13833
+ const pkCols = new Set;
13834
+ if (pkConstraint) {
13835
+ const match = pkConstraint.definition.match(/\(([^)]+)\)/);
13836
+ if (match?.[1]) {
13837
+ match[1].split(",").map((s) => s.trim().replace(/"/g, "")).forEach((c) => pkCols.add(c));
13838
+ }
13839
+ }
13840
+ const fkMap = new Map;
13841
+ for (const c of conRows) {
13842
+ if (c.type === "f") {
13843
+ const match = c.definition.match(/FOREIGN KEY\s*\(([^)]+)\)\s*REFERENCES\s*([^(]+)/i);
13844
+ if (match?.[1] && match?.[2]) {
13845
+ const colName = match[1].trim().replace(/"/g, "");
13846
+ const refTable = match[2].trim().split(".").pop()?.replace(/"/g, "") ?? "";
13847
+ fkMap.set(colName, refTable);
13848
+ }
13849
+ }
13850
+ }
13851
+ return colRows.map((c) => {
13852
+ const isPrimary = pkCols.has(c.column_name) || c.column_name === "id";
13853
+ const ref = fkMap.get(c.column_name);
13854
+ return {
13855
+ name: c.column_name,
13856
+ type: c.data_type,
13857
+ nullable: c.is_nullable === "YES",
13858
+ isPrimary,
13859
+ ...c.column_default ? { defaultValue: c.column_default } : {},
13860
+ isForeignKey: Boolean(ref || c.column_name.endsWith("_id")),
13861
+ ...ref ? { referencesResource: ref } : {}
13862
+ };
13863
+ });
13864
+ }
13865
+ function formatSvadminSchemaExport(table, columns) {
13866
+ const payload = {
13867
+ resource: table,
13868
+ sourceType: "databaseColumns",
13869
+ columns
13870
+ };
13871
+ return `SVAdmin schema for '${table}'
13872
+
13873
+ ` + `Pass this object to svadmin_generateResource. The CLI does not generate the resource.
13874
+ ` + `${JSON.stringify(payload, null, 2)}
13875
+
13876
+ ` + `Column metadata (${columns.length}):
13877
+ ` + columns.map((c) => ` - ${c.name}: ${c.type}${c.isPrimary ? " [PK]" : ""}${c.isForeignKey ? ` [FK -> ${c.referencesResource ?? "inferred"}]` : ""}${c.nullable ? " (nullable)" : " (required)"}`).join(`
13878
+ `);
13879
+ }
13716
13880
  function formatExtensionList(data) {
13717
13881
  const rows = responseRows(data, _Object_({ name: String2(), version: String2(), schema: String2() }));
13718
13882
  if (!rows.length)
@@ -15533,7 +15697,7 @@ function scaffoldFiles(framework, slug) {
15533
15697
  "package.json": JSON.stringify({
15534
15698
  private: true,
15535
15699
  type: "module",
15536
- dependencies: { elysia: "2.0.0-rc.0" }
15700
+ dependencies: { elysia: "2.0.0-rc.1" }
15537
15701
  }, null, 2) + `
15538
15702
  `,
15539
15703
  "index.ts": `import { Elysia } from "elysia";
@@ -20705,13 +20869,13 @@ function registerBranchTools(server, http, options = {}) {
20705
20869
  data_mode: optional(stringEnum2(["schema_only", "full_clone"]), "[create] Preview data mode (default: schema_only)"),
20706
20870
  plan_checksum: optional(String2(), "[promote] Reviewed plan checksum from promotion_plan"),
20707
20871
  confirm_destructive: optional(Boolean2(), "[promote] Confirm reviewed destructive migrations"),
20708
- json: optional(Boolean2(), "[promotion_plan/promote] Return machine-readable JSON")
20872
+ format: optional(stringEnum2(["text", "json"]), "[promotion_plan/promote] Output format (default: text)")
20709
20873
  }, async (args) => {
20710
20874
  const action = requireString(args.action, "action");
20711
20875
  const ref = resolveProjectRef(args.ref, options.projectRef);
20712
20876
  const writeAction = action === "create" || action === "delete" || action === "promote";
20713
20877
  if (writeAction && options.readOnly) {
20714
- return args.json && action === "promote" ? releaseControlFailure("branch.promote", "MUTATION_NOT_SUCCEEDED", null, {
20878
+ return args.format === "json" && action === "promote" ? releaseControlFailure("branch.promote", "MUTATION_NOT_SUCCEEDED", null, {
20715
20879
  project_ref: ref,
20716
20880
  branch_ref: args.branch_ref,
20717
20881
  reason: "READ_ONLY"
@@ -20737,13 +20901,13 @@ function registerBranchTools(server, http, options = {}) {
20737
20901
  responseTimeoutMs: 5000
20738
20902
  });
20739
20903
  if (!result.ok) {
20740
- return promotionFailure(releaseControlFailure("branch.promotion_plan", "HTTP_ERROR", result.transportError ? null : result.status, scope), args.json === true, false);
20904
+ return promotionFailure(releaseControlFailure("branch.promotion_plan", "HTTP_ERROR", result.transportError ? null : result.status, scope), args.format === "json", false);
20741
20905
  }
20742
20906
  const plan = parsePromotionPlan(result.data, { parentRef: ref, branchRef });
20743
20907
  if (result.status !== 200 || !plan) {
20744
- return promotionFailure(releaseControlFailure("branch.promotion_plan", "INVALID_RESPONSE", result.status, scope), args.json === true, false);
20908
+ return promotionFailure(releaseControlFailure("branch.promotion_plan", "INVALID_RESPONSE", result.status, scope), args.format === "json", false);
20745
20909
  }
20746
- return args.json ? releaseControlSuccess("branch.promotion_plan", { ...scope, plan }) : { content: [{ type: "text", text: formatPromotionPlan(plan) }] };
20910
+ return args.format === "json" ? releaseControlSuccess("branch.promotion_plan", { ...scope, plan }) : { content: [{ type: "text", text: formatPromotionPlan(plan) }] };
20747
20911
  } else if (action === "promote") {
20748
20912
  const branchRef = requireString(args.branch_ref, "branch_ref");
20749
20913
  const planChecksum = requireString(args.plan_checksum, "plan_checksum");
@@ -20762,18 +20926,18 @@ function registerBranchTools(server, http, options = {}) {
20762
20926
  });
20763
20927
  const beforeMutation = { ...state, mutation_sent: false };
20764
20928
  if (!preflight.ok) {
20765
- return promotionFailure(releaseControlFailure("branch.promote", "HTTP_ERROR", preflight.transportError ? null : preflight.status, { ...beforeMutation, reason: "PREFLIGHT_FAILED" }), args.json === true, false);
20929
+ return promotionFailure(releaseControlFailure("branch.promote", "HTTP_ERROR", preflight.transportError ? null : preflight.status, { ...beforeMutation, reason: "PREFLIGHT_FAILED" }), args.format === "json", false);
20766
20930
  }
20767
20931
  const plan = parsePromotionPlan(preflight.data, { parentRef: ref, branchRef });
20768
20932
  if (preflight.status !== 200 || !plan) {
20769
- return promotionFailure(releaseControlFailure("branch.promote", "INVALID_RESPONSE", preflight.status, { ...beforeMutation, reason: "PREFLIGHT_UNVERIFIED" }), args.json === true, false);
20933
+ return promotionFailure(releaseControlFailure("branch.promote", "INVALID_RESPONSE", preflight.status, { ...beforeMutation, reason: "PREFLIGHT_UNVERIFIED" }), args.format === "json", false);
20770
20934
  }
20771
20935
  const preflightReason = plan.plan_checksum !== planChecksum ? "PLAN_CHANGED" : !plan.safe_to_apply ? "PLAN_BLOCKED" : plan.requires_destructive_confirmation && args.confirm_destructive !== true ? "DESTRUCTIVE_CONFIRMATION_REQUIRED" : null;
20772
20936
  if (preflightReason) {
20773
- return promotionFailure(releaseControlFailure("branch.promote", "MUTATION_NOT_SUCCEEDED", null, { ...beforeMutation, reason: preflightReason, observed_plan_checksum: plan.plan_checksum }), args.json === true, false);
20937
+ return promotionFailure(releaseControlFailure("branch.promote", "MUTATION_NOT_SUCCEEDED", null, { ...beforeMutation, reason: preflightReason, observed_plan_checksum: plan.plan_checksum }), args.format === "json", false);
20774
20938
  }
20775
20939
  if (plan.pending.length === 0) {
20776
- return args.json ? releaseControlSuccess("branch.promote", {
20940
+ return args.format === "json" ? releaseControlSuccess("branch.promote", {
20777
20941
  ...beforeMutation,
20778
20942
  promoted: false,
20779
20943
  unchanged: true,
@@ -20799,13 +20963,13 @@ function registerBranchTools(server, http, options = {}) {
20799
20963
  mutation_sent: true,
20800
20964
  reported_applied_versions: appliedVersions(result),
20801
20965
  ...safePromotionCode(result) ? { server_code: safePromotionCode(result) } : {}
20802
- }), args.json === true, result.transportError === true || result.responseReadError === true || result.status === 408 || result.status >= 500);
20966
+ }), args.format === "json", result.transportError === true || result.responseReadError === true || result.status === 408 || result.status >= 500);
20803
20967
  }
20804
20968
  const promoted = parsePromotionResult(result.data, { parentRef: ref, branchRef }, plan.pending);
20805
20969
  if (result.status !== 200 || !promoted) {
20806
- return promotionFailure(releaseControlFailure("branch.promote", "OUTCOME_UNKNOWN", result.status, { ...state, mutation_sent: true }), args.json === true, true);
20970
+ return promotionFailure(releaseControlFailure("branch.promote", "OUTCOME_UNKNOWN", result.status, { ...state, mutation_sent: true }), args.format === "json", true);
20807
20971
  }
20808
- return args.json ? releaseControlSuccess("branch.promote", {
20972
+ return args.format === "json" ? releaseControlSuccess("branch.promote", {
20809
20973
  ...promoted,
20810
20974
  unchanged: false,
20811
20975
  mutation_sent: true,
@@ -21478,7 +21642,8 @@ function buildLiteArgs(request) {
21478
21642
  booleanFlag(args, "--service-role", request.service_role);
21479
21643
  booleanFlag(args, "--force", request.force);
21480
21644
  booleanFlag(args, "--memory", request.memory);
21481
- booleanFlag(args, "--json", request.json);
21645
+ if (request.format === "json")
21646
+ args.push("--json");
21482
21647
  if (request.baseline && request.action !== "db_pull")
21483
21648
  throw new Error("baseline requires db_pull");
21484
21649
  booleanFlag(args, "--baseline", request.baseline);
@@ -21603,7 +21768,7 @@ function registerLiteCliTools(server, options = {}) {
21603
21768
  service_role: optional(Boolean2(), "[keys] Also print the service_role key"),
21604
21769
  force: optional(Boolean2(), "[snapshot_restore] Replace non-empty restore targets"),
21605
21770
  memory: optional(Boolean2(), "[*] Use an in-memory PGlite database"),
21606
- json: optional(Boolean2(), "[doctor] Emit machine-readable output"),
21771
+ format: optional(stringEnum2(["text", "json"]), "[doctor] Output format (default: text)"),
21607
21772
  baseline: optional(Boolean2(), "[db_pull] Record already-present schema in local migration history after review"),
21608
21773
  production: optional(Boolean2(), "[doctor] Add single-node production readiness checks"),
21609
21774
  runtime_mode: optional(stringEnum2(["development", "strict"]), "[*] Lite runtime mode"),
@@ -21762,6 +21927,26 @@ function resolveDefaultCodexSkillRoot(environment = process.env, homeDirectory =
21762
21927
  const codexHome = environment.CODEX_HOME?.trim();
21763
21928
  return join8(resolve12(codexHome || join8(homeDirectory, ".codex")), "skills");
21764
21929
  }
21930
+ var SKILL_TARGET_ALIASES = ["codex", "claude", "agents", "project"];
21931
+ function resolveSkillTargetRoot(target, options = {}) {
21932
+ const environment = options.environment ?? process.env;
21933
+ const homeDirectory = options.homeDirectory ?? homedir2();
21934
+ const workingDirectory = options.workingDirectory ?? process.cwd();
21935
+ const normalized = target?.trim().toLowerCase();
21936
+ if (!normalized || normalized === "codex")
21937
+ return resolveDefaultCodexSkillRoot(environment, homeDirectory);
21938
+ if (normalized === "claude") {
21939
+ const config = environment.CLAUDE_CONFIG_DIR?.trim();
21940
+ return join8(resolve12(config || join8(homeDirectory, ".claude")), "skills");
21941
+ }
21942
+ if (normalized === "agents") {
21943
+ const config = environment.AGENTS_HOME?.trim();
21944
+ return join8(resolve12(config || join8(homeDirectory, ".agents")), "skills");
21945
+ }
21946
+ if (normalized === "project")
21947
+ return join8(resolve12(workingDirectory), ".agents", "skills");
21948
+ return resolve12(target);
21949
+ }
21765
21950
  function resolveBundledSkillDirectory(moduleUrl = import.meta.url) {
21766
21951
  const moduleDirectory = dirname5(fileURLToPath(moduleUrl));
21767
21952
  const candidates = [
@@ -21779,7 +21964,7 @@ function textResponse(payload) {
21779
21964
  function registerAiTools(server) {
21780
21965
  registerTool(server, "ai", "Install or inspect the bundled SupaCloud CLI AI skill. Actions: show_skill, install_skill", {
21781
21966
  action: stringEnum2(["show_skill", "install_skill"]),
21782
- target: optional(String2(), "[install_skill] Skill root directory; defaults to $CODEX_HOME/skills or ~/.codex/skills"),
21967
+ target: optional(String2(), "[install_skill] codex|claude|agents|project or an explicit Skill root directory"),
21783
21968
  dry_run: optional(Boolean2(), "[install_skill] Preview the installation without writing files"),
21784
21969
  force: optional(Boolean2(), "[install_skill] Back up and replace different existing skill content")
21785
21970
  }, async (request) => {
@@ -21790,12 +21975,17 @@ function registerAiTools(server) {
21790
21975
  name: SKILL_NAME,
21791
21976
  sourceDirectory,
21792
21977
  defaultTargetRoot,
21793
- defaultDestination: join8(defaultTargetRoot, SKILL_NAME)
21978
+ defaultDestination: join8(defaultTargetRoot, SKILL_NAME),
21979
+ targetAliases: Object.fromEntries(SKILL_TARGET_ALIASES.map((alias) => [
21980
+ alias,
21981
+ resolveSkillTargetRoot(alias)
21982
+ ]))
21794
21983
  });
21795
21984
  }
21985
+ const targetRoot = resolveSkillTargetRoot(request.target);
21796
21986
  return textResponse(installSkill({
21797
21987
  sourceDirectory,
21798
- targetRoot: request.target || defaultTargetRoot,
21988
+ targetRoot,
21799
21989
  mode: request.dry_run ? "dry-run" : "write",
21800
21990
  conflictPolicy: request.force ? "replace" : "reject",
21801
21991
  now: new Date
@@ -21995,16 +22185,16 @@ var starter_metadata_default = {
21995
22185
  generatedBy: "supacloud/starter-metadata-v1",
21996
22186
  packages: {
21997
22187
  compiler: {
21998
- version: "0.53.0"
22188
+ version: "0.54.0"
21999
22189
  },
22000
22190
  app: {
22001
- version: "0.35.0",
22191
+ version: "0.36.0",
22002
22192
  dependencies: {
22003
22193
  rxjs: "7.8.2"
22004
22194
  }
22005
22195
  },
22006
22196
  elysia: {
22007
- version: "0.37.0"
22197
+ version: "0.38.0"
22008
22198
  },
22009
22199
  commands: {
22010
22200
  version: "0.11.2"
@@ -22013,7 +22203,7 @@ var starter_metadata_default = {
22013
22203
  version: "0.15.2"
22014
22204
  },
22015
22205
  db: {
22016
- version: "0.20.3",
22206
+ version: "0.20.4",
22017
22207
  peerDependencies: {
22018
22208
  "drizzle-orm": "1.0.0-rc.5-5935859"
22019
22209
  },
@@ -22099,6 +22289,10 @@ requirement and equivalent lifecycle tests.
22099
22289
  Bind subscriptions to an explicit owner and propagate cancellation to native I/O.
22100
22290
  Never automatically retry a write or turn observable delivery into durable ack.
22101
22291
  Do not bypass authorization, transactions, idempotency or audit to simplify code.
22292
+ Start with \`supacloud app context --format json\` and \`supacloud app doctor --format json\`;
22293
+ do not scan the whole repository first or treat generated/ as source. Apply only
22294
+ reviewed diagnostic fixes with \`supacloud app fix --fix <file>\`, then re-run check
22295
+ and doctor. Never weaken compiler governance to hide a diagnostic.
22102
22296
  Run bun run check; include negative and cleanup tests. Never store live credentials.
22103
22297
  `;
22104
22298
  var STARTER_REACTIVE_TEST = `import { expect, test } from "bun:test";
@@ -22340,7 +22534,6 @@ export function createCompiledModules(): never {
22340
22534
  }
22341
22535
  `;
22342
22536
  function baseFiles(name, sdkDependencies) {
22343
- const minimal = sdkDependencies !== undefined;
22344
22537
  return {
22345
22538
  "package.json": json({
22346
22539
  name,
@@ -22366,8 +22559,8 @@ function baseFiles(name, sdkDependencies) {
22366
22559
  "@supacloud/app": `^${appMetadata.version}`,
22367
22560
  "@supacloud/elysia": `^${elysiaMetadata.version}`,
22368
22561
  effect: "4.0.5",
22369
- elysia: "2.0.0-rc.0",
22370
- ...sdkDependencies ?? { rxjs: appMetadata.dependencies.rxjs }
22562
+ elysia: "2.0.0-rc.1",
22563
+ ...sdkDependencies
22371
22564
  },
22372
22565
  devDependencies: {
22373
22566
  "@supacloud/compiler": `^${compilerMetadata.version}`,
@@ -22412,253 +22605,16 @@ PORT=3000
22412
22605
  `,
22413
22606
  "supacloud.config.ts": `import { defineSupacloudConfig } from "@supacloud/compiler";
22414
22607
 
22415
- export default defineSupacloudConfig({
22416
- root: "src",
22417
- outDir: "generated",
22418
- strict: true,
22419
- moduleBoundaryPreset: "modular-monolith",
22420
- effect: {
22421
- requireRouteEffects: true,
22422
- requireErrorMappings: true,
22423
- requireDependencies: true,
22424
- requireTaggedErrorTypes: true,
22425
- requireExactDependencyTypes: true,
22426
- requireTimeoutForDependencies: true,
22427
- forbidDirectRuntimeExecution: true,
22428
- forbidDirectThrows: true,
22429
- },
22430
- typeSafety: { scanProductionSource: true, noAnyInGenerated: true },
22431
- disallowControllerDirectDb: true,
22432
- detectOrphanModules: true,
22433
- commandCapabilities: { permission: true, transaction: true, idempotency: true, audit: true },
22434
- });
22608
+ export default defineSupacloudConfig();
22435
22609
  `,
22436
22610
  "src/application.ts": APPLICATION_SOURCE(name),
22437
22611
  "generated/application.ts": GENERATED_BOOTSTRAP,
22438
- ...minimal ? {} : {
22439
- "REACTIVE.md": STARTER_REACTIVE_GUIDE,
22440
- "AGENTS.md": STARTER_REACTIVE_AGENTS,
22441
- "tests/reactive.test.ts": STARTER_REACTIVE_TEST
22442
- },
22443
22612
  "scripts/environment.ts": STARTER_ENVIRONMENT,
22444
22613
  "tests/environment.test.ts": STARTER_ENVIRONMENT_TEST,
22445
22614
  "scripts/dev.ts": DEV_SCRIPT,
22446
22615
  "scripts/serve.ts": SERVE_SCRIPT
22447
22616
  };
22448
22617
  }
22449
- function httpTemplate(name) {
22450
- return {
22451
- ...baseFiles(name),
22452
- "src/orders/contracts.ts": `import { t } from "elysia";
22453
- export const OrderParams = t.Object({ id: t.String({ minLength: 1 }) });
22454
- export const CreateOrderBody = t.Object({ name: t.String({ minLength: 1 }) });
22455
- export const OrderResult = t.Object({ id: t.String(), name: t.String() });
22456
- export const HealthResult = t.Object({ ok: t.Boolean() });
22457
- `,
22458
- "src/orders/orders.ts": `import { Effect } from "effect";
22459
- import { Body, Controller, Get, Param, Post, defineFeatureSlice } from "@supacloud/app";
22460
- import { status } from "elysia";
22461
- import { OrderParams, CreateOrderBody, OrderResult, HealthResult } from "./contracts";
22462
- @Controller("/orders")
22463
- export class OrdersController {
22464
- @Get("/health", {
22465
- responses: { 200: HealthResult },
22466
- effect: { required: true, dependencies: [], errors: [], retry: "none" },
22467
- })
22468
- health(): Effect.Effect<{ ok: boolean }, never, never> { return Effect.succeed({ ok: true }); }
22469
-
22470
- @Get("/:id", {
22471
- params: OrderParams,
22472
- responses: { 200: OrderResult },
22473
- effect: { required: true, dependencies: [], errors: [], retry: "none" },
22474
- })
22475
- get(@Param("id") id: string): Effect.Effect<{ id: string; name: string }, never, never> {
22476
- return Effect.succeed({ id, name: "demo" });
22477
- }
22478
-
22479
- @Post("/", {
22480
- body: CreateOrderBody,
22481
- responses: { 201: OrderResult },
22482
- effect: { required: true, dependencies: [], errors: [], retry: "none" },
22483
- })
22484
- create(@Body() body: { name: string }) {
22485
- return Effect.succeed(status(201, { id: "demo", name: body.name }));
22486
- }
22487
- }
22488
-
22489
- export const OrdersFeature = defineFeatureSlice({
22490
- name: "orders",
22491
- tags: ["type:feature"],
22492
- controllers: [OrdersController],
22493
- });
22494
- `,
22495
- "scripts/sandbox.ts": `import { createMemorySandbox } from "@supacloud/elysia";
22496
- import { createCompiledModules } from "../generated/application";
22497
-
22498
- export function createDemo() {
22499
- if (process.env["APP_ENV"] !== "development" && process.env["APP_ENV"] !== "test") {
22500
- throw new Error("Memory adapters are restricted to development and test");
22501
- }
22502
- return createMemorySandbox({
22503
- modules: createCompiledModules(),
22504
- identity: { authenticated: true, subject: "local-demo" },
22505
- });
22506
- }
22507
- `,
22508
- "tests/orders.test.ts": `import { expect, test } from "bun:test";
22509
- import { createDemo } from "../scripts/sandbox";
22510
-
22511
- test("health, read and create routes follow the declared HTTP contract", async () => {
22512
- const sandbox = createDemo();
22513
- expect((await sandbox.request("/orders/health")).status).toBe(200);
22514
- expect((await sandbox.request("/orders/1")).status).toBe(200);
22515
- const created = await sandbox.request("/orders", {
22516
- method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ name: "first" }),
22517
- });
22518
- expect(created.status).toBe(201);
22519
- expect(await created.json()).toEqual({ id: "demo", name: "first" });
22520
- const invalid = await sandbox.request("/orders", {
22521
- method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({}),
22522
- });
22523
- expect(invalid.status).toBe(422);
22524
- });
22525
- `,
22526
- "README.md": `# ${name}
22527
-
22528
- HTTP API golden path.
22529
-
22530
- ## Local Development
22531
-
22532
- \`\`\`sh
22533
- bun install
22534
- bun run check
22535
- bun run dev
22536
- \`\`\`
22537
-
22538
- The server binds only to 127.0.0.1:3000 by default.
22539
-
22540
- \`\`\`sh
22541
- curl http://127.0.0.1:3000/orders/health
22542
- curl http://127.0.0.1:3000/orders/1
22543
- curl -X POST http://127.0.0.1:3000/orders \\
22544
- -H 'Content-Type: application/json' -d '{"name":"first"}'
22545
- \`\`\`
22546
-
22547
- The generated feature declares explicit route schemas so the compiler can detect
22548
- route/schema drift. Keep request and response shapes in \`src/orders/orders.ts\`;
22549
- do not widen them to \`unknown\` just to accept unvalidated data.
22550
-
22551
- ## Production Boundary
22552
-
22553
- \`bun run build\` emits an application factory, not a server. Import \`createApp\`
22554
- from \`output/supacloud/application.js\` in your trusted host and supply real dependency,
22555
- request-context and command-governance adapters. The memory sandbox is
22556
- development/test only.
22557
-
22558
- Keep backend bundles outside the frontend output directory. Vite normally cleans
22559
- \`dist/\` before building; server artifacts placed there can be deleted or included
22560
- in a public frontend archive. Use disjoint output directories, not a disabled
22561
- frontend clean step. Compiler source artifacts remain in \`generated/\`.
22562
-
22563
- ## Inspection And Repair
22564
-
22565
- \`\`\`sh
22566
- supacloud context --root . --target orders --format json
22567
- supacloud doctor --root .
22568
- \`\`\`
22569
- `
22570
- };
22571
- }
22572
- function edgeTemplate(name) {
22573
- return {
22574
- ...baseFiles(name),
22575
- "src/sync/sync.ts": `import { Injectable, Job, defineFeatureSlice } from "@supacloud/app";
22576
-
22577
- /**
22578
- * Worker/edge golden path: a declared, retry-bounded job with an explicit
22579
- * idempotency requirement. The platform owns scheduling, retry and DLQ; the
22580
- * handler stays side-effect explicit and must not assume exactly-once execution.
22581
- */
22582
- @Injectable()
22583
- @Job({
22584
- name: "sync.orders",
22585
- mode: "task",
22586
- idempotency: "required",
22587
- timeoutSec: 60,
22588
- maxAttempts: 3,
22589
- })
22590
- export class SyncOrdersJob {
22591
- async run(): Promise<{ ok: boolean }> {
22592
- return { ok: true };
22593
- }
22594
- }
22595
-
22596
- export const SyncFeature = defineFeatureSlice({
22597
- name: "sync",
22598
- tags: ["type:feature"],
22599
- providers: [SyncOrdersJob],
22600
- jobs: [SyncOrdersJob],
22601
- });
22602
- `,
22603
- "scripts/sandbox.ts": `import { createMemorySandbox } from "@supacloud/elysia";
22604
- import { createCompiledModules } from "../generated/application";
22605
-
22606
- export function createDemo() {
22607
- if (process.env["APP_ENV"] !== "development" && process.env["APP_ENV"] !== "test") {
22608
- throw new Error("Memory adapters are restricted to development and test");
22609
- }
22610
- return createMemorySandbox({
22611
- modules: createCompiledModules(),
22612
- identity: { authenticated: true, subject: "local-worker" },
22613
- });
22614
- }
22615
- `,
22616
- "tests/sync.test.ts": `import { expect, test } from "bun:test";
22617
- import { SyncOrdersJob } from "../src/sync/sync";
22618
-
22619
- test("the declared job handler is deterministic and idempotent by construction", async () => {
22620
- const job = new SyncOrdersJob();
22621
- expect(await job.run()).toEqual({ ok: true });
22622
- expect(await job.run()).toEqual({ ok: true });
22623
- });
22624
- `,
22625
- "README.md": `# ${name}
22626
-
22627
- Worker / Edge golden path.
22628
-
22629
- ## Local Development
22630
-
22631
- \`\`\`sh
22632
- bun install
22633
- bun run check
22634
- bun run test
22635
- \`\`\`
22636
-
22637
- \`src/sync/sync.ts\` declares a platform job with an explicit mode, timeout,
22638
- attempt bound and idempotency requirement. The platform schedules and retries;
22639
- the handler must be safe to run more than once and must not assume exactly-once
22640
- execution.
22641
-
22642
- ## Production Boundary
22643
-
22644
- The job runs through the platform worker/edge adapter, not the local memory
22645
- sandbox. Bind persistence, authorization and audit to durable adapters before
22646
- invoking it in production. Do not schedule destructive work without an
22647
- idempotency key.
22648
-
22649
- \`bun run build\` emits \`output/supacloud/application.js\`, an application factory,
22650
- not a deployed Worker. Keep it outside any frontend output directory such as
22651
- \`dist/\`; frontend clean steps and public archives must not own server bundles.
22652
-
22653
- ## Inspection And Repair
22654
-
22655
- \`\`\`sh
22656
- supacloud context --root . --target sync --format json
22657
- supacloud doctor --root .
22658
- \`\`\`
22659
- `
22660
- };
22661
- }
22662
22618
  function minimalTemplate(name, sdkDependencies) {
22663
22619
  return {
22664
22620
  ...baseFiles(name, sdkDependencies),
@@ -22713,8 +22669,12 @@ export function createDemo() {
22713
22669
  - Cross-table business writes require trusted authorization, transactions,
22714
22670
  idempotency and audit. Never put service-role or Management tokens in a browser.
22715
22671
  - Do not edit generated/. Do not rewrite historical migrations.
22672
+ - Start with \`supacloud app context --format json\` and \`supacloud app doctor --format json\`.
22673
+ Do not scan the whole repository first or treat generated/ as source.
22674
+ - Apply only reviewed diagnostic fixes with \`supacloud app fix --fix <file>\`, then
22675
+ re-run check and doctor. Never weaken compiler governance to hide a diagnostic.
22716
22676
  - Run the directly related single test file and git diff --check for daily changes.
22717
- - Use app verify-plan --target <module> to inspect the focused verification plan.
22677
+ - Use \`supacloud app verify-plan --target <module>\` to inspect the focused verification plan.
22718
22678
  - Do not introduce another workflow engine, DI container or session store.
22719
22679
  `,
22720
22680
  "README.md": `# ${name}
@@ -22748,8 +22708,8 @@ Use fixed trusted HTTPS endpoints and never retry an uncertain write.
22748
22708
 
22749
22709
  GraphQL is optional; this starter has no synthetic GraphQL schema.
22750
22710
  For a complete persistent command/attachment example, create a separate reference
22751
- project with app init --template command. The http and edge recipes also remain
22752
- explicit options; do not install every recipe into this application.
22711
+ project with app init --template command. Do not install every recipe into this
22712
+ application.
22753
22713
 
22754
22714
  ## Integration and delivery
22755
22715
 
@@ -22766,58 +22726,9 @@ regeneration, then validate types, database permissions and authenticated behavi
22766
22726
  };
22767
22727
  }
22768
22728
  function appTemplateFiles(name, template, sdkDependencies) {
22769
- if (template === "minimal") {
22770
- if (!sdkDependencies)
22771
- throw new Error("Minimal template requires the initializer's SDK dependency versions");
22772
- return minimalTemplate(name, sdkDependencies);
22773
- }
22774
- if (template !== "http" && template !== "edge")
22729
+ if (template !== "minimal")
22775
22730
  throw new Error("Use appStarterFiles for the command recipe");
22776
- const files = template === "http" ? httpTemplate(name) : edgeTemplate(name);
22777
- const feature = template === "http" ? "OrdersFeature" : "SyncFeature";
22778
- const source = template === "http" ? "./orders/orders" : "./sync/sync";
22779
- return {
22780
- ...files,
22781
- "src/app.module.ts": `import { Module } from "@supacloud/app";
22782
- import { ${feature} } from "${source}";
22783
-
22784
- // Compose feature modules here. Do not make unrelated features import each other.
22785
- @Module({
22786
- name: "application-root",
22787
- tags: ["type:app"],
22788
- imports: [${feature}],
22789
- })
22790
- export class AppModule {}
22791
- `,
22792
- "README.md": files["README.md"] + `
22793
- ## Application Composition
22794
-
22795
- \`src/app.module.ts\` is the application composition root. Register additional
22796
- feature modules there, rather than importing them from another feature.
22797
- The compiler discovers this declaration; no runtime container lookup is needed.
22798
-
22799
- With the matching candidate/released CLI installed:
22800
-
22801
- \`\`\`sh
22802
- supacloud app generate --kind resource --name inventory --register-in src/app.module.ts --dry-run --format json
22803
- supacloud app generate --kind resource --name inventory --register-in src/app.module.ts
22804
- bun run check
22805
- bun run inspect
22806
- \`\`\`
22807
-
22808
- The resource is an asynchronous read skeleton, not implemented CRUD. Its Service
22809
- rejects until an authorized data adapter is supplied; implement it and replace the
22810
- placeholder test together. Generation does not grant access or connect a database.
22811
-
22812
- \`bun run inspect\` delegates to the installed compiler and prints the current
22813
- source graph as JSON without starting the app or regenerating artifacts. This is
22814
- not a live mounted-route report. Configuration remains trusted project code.
22815
- After changing source, compile before using artifact-backed CLI graph/explain.
22816
-
22817
- In CI, run \`bun run check:generated\` before commands that regenerate artifacts;
22818
- \`bun run check\` is the convenient bootstrap path for a newly created project.
22819
- `
22820
- };
22731
+ return minimalTemplate(name, sdkDependencies);
22821
22732
  }
22822
22733
 
22823
22734
  // src/shared/tools/app-starter-review-job.ts
@@ -24118,9 +24029,9 @@ After installing dependencies, run \`bun run db:generate\` once and commit db/co
24118
24029
  The default \`bun run check\` and build fail on stale database contracts before any
24119
24030
  compiler regeneration. After an intentional source change, run db:generate again,
24120
24031
  review the diff, then run check.
24121
- The command starter registers databaseGovernance.moduleFile = "db/modules.ts" and
24122
- keeps the default \`databaseGovernance.mode = "check"\`: compile/check/build run
24123
- the offline source-contract gate without a database. Use \`bun run dev:integration\`
24032
+ The command starter uses the default database governance module path
24033
+ \`db/modules.ts\` and fixes \`databaseGovernance.mode = "check"\`: compile/check/build
24034
+ run the offline source-contract gate without a database. Use \`bun run dev:integration\`
24124
24035
  with the CLI's explicit loopback database URL, or set databaseGovernance.mode to
24125
24036
  "enforce" with an explicitly supplied \`DATABASE_URL\`, to run SQL lint and live
24126
24037
  Catalog reconciliation. Missing connections, stale source contracts and Catalog
@@ -25532,7 +25443,7 @@ function appStarterFiles(name) {
25532
25443
  "@supacloud/js": `^${sdkMetadata.version}`,
25533
25444
  "@supabase/supabase-js": sdkMetadata.peerDependencies["@supabase/supabase-js"],
25534
25445
  effect: "4.0.5",
25535
- elysia: "2.0.0-rc.0",
25446
+ elysia: "2.0.0-rc.1",
25536
25447
  rxjs: appMetadata2.dependencies.rxjs
25537
25448
  },
25538
25449
  devDependencies: {
@@ -25591,25 +25502,7 @@ PORT=3000
25591
25502
  "supacloud.config.ts": `import { defineSupacloudConfig } from "@supacloud/compiler";
25592
25503
 
25593
25504
  export default defineSupacloudConfig({
25594
- root: "src",
25595
- outDir: "generated",
25596
- strict: true,
25597
- moduleBoundaryPreset: "modular-monolith",
25598
- databaseGovernance: { mode: "check", moduleFile: "db/modules.ts", databaseUrlEnv: "DATABASE_URL" },
25599
- effect: {
25600
- requireRouteEffects: true,
25601
- requireErrorMappings: true,
25602
- requireDependencies: true,
25603
- requireTaggedErrorTypes: true,
25604
- requireExactDependencyTypes: true,
25605
- requireTimeoutForDependencies: true,
25606
- forbidDirectRuntimeExecution: true,
25607
- forbidDirectThrows: true,
25608
- },
25609
- typeSafety: { scanProductionSource: true, noAnyInGenerated: true },
25610
- disallowControllerDirectDb: true,
25611
- detectOrphanModules: true,
25612
- commandCapabilities: { permission: true, transaction: true, idempotency: true, audit: true },
25505
+ databaseGovernance: { mode: "check" },
25613
25506
  delivery: {
25614
25507
  version: 1,
25615
25508
  build: {
@@ -26400,7 +26293,7 @@ and static execution plans, without provider values or source expressions.
26400
26293
  The app context entry point retains its module workflow and adds a separate
26401
26294
  execution evidence block when --events and --request-id are provided together.
26402
26295
  It requires an explicit --target and --format json, rejects --write, and
26403
- resolves relative event paths from --root. The top-level context alias supports
26296
+ resolves relative event paths from --root. The app context action supports
26404
26297
  the same flags. Omit both execution flags for the unchanged authoring context.
26405
26298
  The optional --events example expects a trusted host
26406
26299
  to capture approved onExecution metadata in { "version": 1, "events": [...] }.
@@ -28159,13 +28052,27 @@ async function initProject(args) {
28159
28052
  throw new Error("app init never overwrites files; choose an empty directory");
28160
28053
  const template = args.template ?? "minimal";
28161
28054
  const { root, name, files } = await initializeAppProject({ root: args.root, name: args.name, template });
28162
- return textResult([
28163
- `Initialized ${name} in ${root} (${files.length} files, template: ${template}).`,
28164
- `cd '${root.replaceAll("'", "'\\''")}'`,
28055
+ const next = [
28165
28056
  "bun install",
28166
28057
  ...template === "command" ? ["bun run db:generate"] : [],
28167
28058
  "bun run check",
28168
- "bun run dev",
28059
+ "bun run dev"
28060
+ ];
28061
+ if (args.format === "json")
28062
+ return textResult(JSON.stringify({
28063
+ version: 1,
28064
+ ok: true,
28065
+ root,
28066
+ name,
28067
+ template,
28068
+ files,
28069
+ next,
28070
+ note: "Local demo only. Production requires persistent governance and identity adapters; see README.md."
28071
+ }, null, 2));
28072
+ return textResult([
28073
+ `Initialized ${name} in ${root} (${files.length} files, template: ${template}).`,
28074
+ `cd '${root.replaceAll("'", "'\\''")}'`,
28075
+ ...next,
28169
28076
  "Local demo only. Production requires persistent governance and identity adapters; see README.md."
28170
28077
  ].join(`
28171
28078
  `));
@@ -28564,9 +28471,9 @@ async function runContext(args) {
28564
28471
  `));
28565
28472
  }
28566
28473
  function doctorFixPlan(doctor) {
28567
- return createDiagnosticRepairPlan(doctor.diagnostics ?? []).map((repair) => ({
28474
+ return createDiagnosticRepairPlan(doctor.diagnostics ?? []).map((repair, index) => ({
28568
28475
  ...repair,
28569
- command: "supacloud app fix --fix <fix.json>"
28476
+ command: `supacloud app fix --from-doctor <doctor.json> --fix-index ${index}`
28570
28477
  }));
28571
28478
  }
28572
28479
  async function findUnwiredContracts(rootDir) {
@@ -28622,6 +28529,11 @@ async function runDoctor(args) {
28622
28529
  const autoFixable = fixPlan.filter((repair) => repair.readiness === "preview").length;
28623
28530
  const inputRequired = fixPlan.filter((repair) => repair.readiness === "input-required").length;
28624
28531
  const manualFixes = fixPlan.filter((repair) => repair.readiness === "manual").length;
28532
+ const next = doctor.errors === 0 ? ["supacloud app compile", "supacloud app verify-plan --target <module>"] : [
28533
+ ...fixPlan.filter((repair) => repair.readiness === "preview").slice(0, 1).map((repair) => repair.command),
28534
+ "supacloud app check --format json",
28535
+ "supacloud app doctor --format json"
28536
+ ];
28625
28537
  const unwiredContracts = context ? [] : await findUnwiredContracts(root);
28626
28538
  const scope = context ? {
28627
28539
  target: context.subject,
@@ -28633,6 +28545,7 @@ async function runDoctor(args) {
28633
28545
  if (args.format === "json") {
28634
28546
  return textResult(JSON.stringify({
28635
28547
  ok: doctor.errors === 0,
28548
+ next,
28636
28549
  autoFixable,
28637
28550
  inputRequired,
28638
28551
  manualFixes,
@@ -28651,7 +28564,7 @@ async function runDoctor(args) {
28651
28564
  lines.push("");
28652
28565
  lines.push(doctor.errors === 0 ? "No blocking issues. Run `supacloud app compile` to refresh generated artifacts." : `${doctor.errors} blocking issue(s). Run \`supacloud app check\` for the full diagnostic list.`);
28653
28566
  if (autoFixable > 0) {
28654
- lines.push(`${autoFixable} preview-ready fix(es): run \`supacloud app doctor --format json\`, save each \`fix\`, then preview with \`supacloud app fix --fix <fix.json>\`. Add --write only after review.`);
28567
+ lines.push(`${autoFixable} preview-ready fix(es): run \`supacloud app doctor --format json > doctor.json\`, then preview with \`supacloud app fix --from-doctor doctor.json --fix-index <n>\`. Add --write only after review.`);
28655
28568
  }
28656
28569
  if (inputRequired > 0 || manualFixes > 0) {
28657
28570
  lines.push(`${inputRequired} fix(es) require explicit input; ${manualFixes} require manual implementation.`);
@@ -28668,13 +28581,49 @@ async function runDoctor(args) {
28668
28581
  async function runFix(args) {
28669
28582
  const root = resolve18(args.root || process.cwd());
28670
28583
  const fixPath = args.fix?.trim();
28671
- if (!fixPath)
28672
- throw new Error("app fix requires --fix <path-to-diagnostic-fix.json>");
28673
- const parsed = JSON.parse(await readFile4(resolve18(root, fixPath), "utf8"));
28584
+ const doctorPath = args.from_doctor?.trim();
28585
+ if (fixPath && doctorPath)
28586
+ throw new Error("app fix accepts either --fix or --from-doctor, not both");
28587
+ if (!fixPath && !doctorPath)
28588
+ throw new Error("app fix requires --fix <fix.json> or --from-doctor <doctor.json> --fix-index <n>");
28589
+ let parsed;
28590
+ let source = "fix";
28591
+ let repair;
28592
+ if (doctorPath) {
28593
+ const rawIndex = args.fix_index;
28594
+ if (!Number.isSafeInteger(rawIndex) || rawIndex === undefined || rawIndex < 0) {
28595
+ throw new Error("app fix --from-doctor requires a non-negative --fix-index <n>");
28596
+ }
28597
+ const doctor = JSON.parse(await readFile4(resolve18(root, doctorPath), "utf8"));
28598
+ if (!doctor || typeof doctor !== "object" || !Array.isArray(doctor.fixPlan)) {
28599
+ throw new Error("Invalid doctor output: expected a fixPlan array");
28600
+ }
28601
+ const plan = doctor.fixPlan;
28602
+ if (rawIndex >= plan.length)
28603
+ throw new Error(`--fix-index ${rawIndex} is outside fixPlan (0..${plan.length - 1})`);
28604
+ const entry = plan[rawIndex];
28605
+ if (!entry || typeof entry !== "object" || !("fix" in entry)) {
28606
+ throw new Error(`fixPlan[${rawIndex}] does not contain a fix payload`);
28607
+ }
28608
+ const repairEntry = entry;
28609
+ if (repairEntry.readiness !== undefined && repairEntry.readiness !== "preview") {
28610
+ throw new Error(`fixPlan[${rawIndex}] is ${repairEntry.readiness}; only preview-ready repairs can be applied automatically`);
28611
+ }
28612
+ repair = repairEntry;
28613
+ parsed = repairEntry.fix;
28614
+ source = "doctor";
28615
+ } else {
28616
+ parsed = JSON.parse(await readFile4(resolve18(root, fixPath), "utf8"));
28617
+ }
28674
28618
  const write = args.write === true;
28675
28619
  const applied = await applyDiagnosticFix(parsed, { rootDir: root, dryRun: !write });
28676
28620
  return textResult(JSON.stringify({
28677
28621
  ok: true,
28622
+ source,
28623
+ ...source === "doctor" ? {
28624
+ index: args.fix_index,
28625
+ ...repair?.code === undefined ? {} : { code: repair.code }
28626
+ } : {},
28678
28627
  written: write,
28679
28628
  file: applied.file,
28680
28629
  changed: applied.changed,
@@ -28894,11 +28843,14 @@ async function runCheck(args) {
28894
28843
  strict: args.strict ?? loadedConfig.strict ?? false
28895
28844
  }, root);
28896
28845
  const result = await checkProject(config);
28897
- const hasError = result.diagnostics.some((diagnostic) => diagnostic.severity === "error") || result.mismatches.length > 0 || database?.ok === false;
28846
+ const diagnosticError = result.diagnostics.some((diagnostic) => diagnostic.severity === "error");
28847
+ const hasError = diagnosticError || result.mismatches.length > 0 || database?.ok === false;
28848
+ const next = hasError ? diagnosticError || database?.ok === false ? ["supacloud app doctor --format json"] : ["supacloud app compile", "supacloud app check --format json"] : ["supacloud app verify-plan --target <module>"];
28898
28849
  if (args.format === "json")
28899
28850
  return textResult(JSON.stringify({
28900
28851
  version: 1,
28901
28852
  ok: !hasError,
28853
+ next,
28902
28854
  source: "declaration",
28903
28855
  diagnostics: result.diagnostics,
28904
28856
  mismatches: result.mismatches,
@@ -29324,7 +29276,7 @@ async function runAppRelease(request, options, delegate) {
29324
29276
  ...typeof deployReceipt.activation_id === "string" ? { activation_id: deployReceipt.activation_id } : {},
29325
29277
  ...deployReceipt.web === undefined ? {} : { web: deployReceipt.web }
29326
29278
  };
29327
- return request.format === "json" || request.json === true ? textResult(JSON.stringify(result, null, 2)) : textResult([
29279
+ return request.format === "json" ? textResult(JSON.stringify(result, null, 2)) : textResult([
29328
29280
  `${ref}/${id}/${environmentId}: ${deployReceipt.status === "accepted" ? "accepted" : result.no_op ? "no-op" : "released"}`,
29329
29281
  `Release: ${releaseId}`,
29330
29282
  `Configuration: ${configurationId}`,
@@ -29527,7 +29479,7 @@ async function runPromotion(request, options, delegate) {
29527
29479
  const ref = request.ref ?? options.projectRef;
29528
29480
  if (request.mutation_id !== undefined)
29529
29481
  throw new Error("app promote generates a new mutation ID; use promote-status or promote-reconcile to inspect an existing mutation");
29530
- const { format, json, ...remoteRequest } = request;
29482
+ const { format, ...remoteRequest } = request;
29531
29483
  const planArgs = {
29532
29484
  ...remoteRequest,
29533
29485
  action: "get_promotion_plan",
@@ -29565,7 +29517,7 @@ async function runPromotion(request, options, delegate) {
29565
29517
  no_op: true,
29566
29518
  execution_performed: false
29567
29519
  });
29568
- return format === "json" || json ? noOp : textResult(`${plan.project_ref}/${plan.application_id}/${plan.environment_id}: no-op
29520
+ return format === "json" ? noOp : textResult(`${plan.project_ref}/${plan.application_id}/${plan.environment_id}: no-op
29569
29521
  Execution: skipped`);
29570
29522
  }
29571
29523
  if (plan.blockers.some((code) => !["MIGRATION_PENDING", "BACKUP_REQUIRED"].includes(code))) {
@@ -29587,23 +29539,9 @@ Execution: skipped`);
29587
29539
  };
29588
29540
  validateToolArguments2(APPLICATION_TOOL_SCHEMA2, executeArgs);
29589
29541
  const result = await delegate(executeArgs);
29590
- return format === "json" || json ? result : formatPromotionExecution(result);
29542
+ return format === "json" ? result : formatPromotionExecution(result);
29591
29543
  }
29592
29544
  async function runAppTool(request, options = {}) {
29593
- if (request.dry_run !== undefined && request["dry-run"] !== undefined || request.register_in !== undefined && request["register-in"] !== undefined || request.database_url !== undefined && request["database-url"] !== undefined || request.database_source !== undefined && request["database-source"] !== undefined || request.database_table !== undefined && request["database-table"] !== undefined || request.database_type !== undefined && request["database-type"] !== undefined || request.read_permission !== undefined && request["read-permission"] !== undefined || request.request_id !== undefined && request["request-id"] !== undefined) {
29594
- throw new ScaffoldError("SCAFFOLD_OPTION_INVALID", "Do not combine hyphenated and underscored aliases of the same flag");
29595
- }
29596
- request = {
29597
- ...request,
29598
- ...request["dry-run"] === undefined ? {} : { dry_run: request["dry-run"] },
29599
- ...request["register-in"] === undefined ? {} : { register_in: request["register-in"] },
29600
- ...request["database-url"] === undefined ? {} : { database_url: request["database-url"] },
29601
- ...request["database-source"] === undefined ? {} : { database_source: request["database-source"] },
29602
- ...request["database-table"] === undefined ? {} : { database_table: request["database-table"] },
29603
- ...request["database-type"] === undefined ? {} : { database_type: request["database-type"] },
29604
- ...request["read-permission"] === undefined ? {} : { read_permission: request["read-permission"] },
29605
- ...request["request-id"] === undefined ? {} : { request_id: request["request-id"] }
29606
- };
29607
29545
  if (request.events !== undefined || request.request_id !== undefined) {
29608
29546
  if (request.action !== "context" || !request.events?.trim() || !request.target?.trim() || !request.request_id || !/^[A-Za-z0-9._:-]{1,256}$/.test(request.request_id) || request.format !== "json" || request.write !== undefined) {
29609
29547
  throw new Error("Execution context requires app context, --target, --events, --request-id and --format json; --write is not supported");
@@ -29615,10 +29553,6 @@ async function runAppTool(request, options = {}) {
29615
29553
  if (request.database_url !== undefined && request.action !== "dev" && request.action !== "watch") {
29616
29554
  throw new ScaffoldError("SCAFFOLD_OPTION_INVALID", "--database-url applies only to app dev --profile integration");
29617
29555
  }
29618
- if (request.json !== undefined && !Object.hasOwn(REMOTE_APP_ACTIONS, request.action) && request.action !== "promote" && request.action !== "release")
29619
- throw new Error("--json applies only to remote app actions");
29620
- if (request.json && request.format === "text")
29621
- throw new Error("--json and --format text cannot be combined");
29622
29556
  if (request.action === "promote") {
29623
29557
  const delegate = options.getApplications?.();
29624
29558
  if (!delegate)
@@ -29637,7 +29571,7 @@ async function runAppTool(request, options = {}) {
29637
29571
  return textResult("App remote actions require a Management API context.", true);
29638
29572
  const promotionDiff = request.action === "diff" && request.source_ref !== undefined && request.source_environment_id !== undefined && request.source_release_id !== undefined;
29639
29573
  const action = promotionDiff ? "get_promotion_plan" : REMOTE_APP_ACTIONS[request.action];
29640
- const { format, json, ...remoteRequest } = request;
29574
+ const { format, ...remoteRequest } = request;
29641
29575
  const args = {
29642
29576
  ...remoteRequest,
29643
29577
  action,
@@ -29645,7 +29579,7 @@ async function runAppTool(request, options = {}) {
29645
29579
  };
29646
29580
  validateToolArguments2(APPLICATION_TOOL_SCHEMA2, args);
29647
29581
  const result = await delegate(args);
29648
- if (request.action === "evidence" && format !== "json" && json !== true && !result.isError) {
29582
+ if (request.action === "evidence" && format !== "json" && !result.isError) {
29649
29583
  try {
29650
29584
  const payload = JSON.parse(result.content[0].text);
29651
29585
  if (payload === null || typeof payload !== "object" || !("project_ref" in payload) || payload.project_ref !== args.ref || !("application_id" in payload) || payload.application_id !== request.id || !("environment_id" in payload) || payload.environment_id !== request.environment_id || !("evidence" in payload))
@@ -29660,17 +29594,17 @@ async function runAppTool(request, options = {}) {
29660
29594
  return textResult("DEPLOYMENT unknown: invalid or mismatched evidence; no mutation was attempted.", true);
29661
29595
  }
29662
29596
  }
29663
- if (request.action === "history" && format !== "json" && json !== true) {
29597
+ if (request.action === "history" && format !== "json") {
29664
29598
  return historyText(result, request, args.ref);
29665
29599
  }
29666
29600
  if ((request.action === "deploy-plan" || request.action === "diff") && format !== "json" && (request.action === "deploy-plan" || request.source_ref === undefined || request.source_environment_id === undefined || request.source_release_id === undefined)) {
29667
29601
  return formatRemoteDeployPlan(result);
29668
29602
  }
29669
29603
  if (request.action === "promote-plan" || request.action === "diff" && request.source_ref !== undefined && request.source_environment_id !== undefined && request.source_release_id !== undefined) {
29670
- if (!result.isError && format !== "json" && !json)
29604
+ if (!result.isError && format !== "json")
29671
29605
  return formatPromotionPlan2(result);
29672
29606
  }
29673
- return (request.action === "transfer-plan" || request.action === "transfer") && format !== "json" && !json ? formatReleaseTransfer(result) : format !== "json" && !json ? formatWorkflowObservation(result, request.action) : result;
29607
+ return (request.action === "transfer-plan" || request.action === "transfer") && format !== "json" ? formatReleaseTransfer(result) : format !== "json" ? formatWorkflowObservation(result, request.action) : result;
29674
29608
  }
29675
29609
  switch (request.action) {
29676
29610
  case "verify-plan": {
@@ -29772,83 +29706,38 @@ function registerAppTools(server, options = {}) {
29772
29706
  "promote-reconcile"
29773
29707
  ]), "App action; promote obtains and verifies its plan before one mutation request"),
29774
29708
  kind: optional(stringEnum2(["module", "command", "query", "controller", "job", "contract", "resource", "feature"]), "[generate] Scaffold kind"),
29775
- template: optional(stringEnum2(["minimal", "http", "command", "edge"]), "[init] Minimal application by default; explicit http/command/edge recipes"),
29709
+ template: optional(stringEnum2(["minimal", "command"]), "[init] Minimal application by default; command adds the persistent governed-command reference"),
29776
29710
  name: optional(String2(), "[init/generate] Project or object name"),
29777
29711
  module: optional(String2(), "[generate] Target feature module (required for command/query/controller)"),
29778
29712
  permission: optional(String2(), "[generate feature] Explicit approval permission; not inferred from name"),
29779
29713
  read_permission: optional(String2(), "[generate feature] Explicit read permission; not inferred from name"),
29780
- "read-permission": optional(String2(), "[generate feature] CLI alias of read_permission"),
29781
29714
  rpc: optional(String2(), "[generate feature] Explicit durable RPC capability; must be registered by the host"),
29782
29715
  database_source: optional(String2(), "[generate feature] Project-relative database source path"),
29783
29716
  database_table: optional(String2(), "[generate feature] Schema-qualified table owned by the feature"),
29784
29717
  database_type: optional(String2(), "[generate feature] Exported row type derived from the database source"),
29785
- "database-source": optional(String2(), "[generate feature] CLI alias of database_source"),
29786
- "database-table": optional(String2(), "[generate feature] CLI alias of database_table"),
29787
- "database-type": optional(String2(), "[generate feature] CLI alias of database_type"),
29788
29718
  dir: optional(String2(), "[generate] Feature root directory (default: src/features)"),
29789
29719
  force: optional(Boolean2(), "[generate] Overwrite a single regular file; resource/feature/controller never overwrite"),
29790
29720
  dry_run: optional(Boolean2(), "[generate] Validate and preview the complete change set without writing"),
29791
- "dry-run": optional(Boolean2(), "[generate] CLI alias of dry_run"),
29792
29721
  register_in: optional(String2(), "[generate] Existing parent module file for module/resource/feature; uses the compiler AST editor"),
29793
- "register-in": optional(String2(), "[generate] CLI alias of register_in"),
29794
29722
  root: optional(String2(), "[dev/plan/build/release/compile/check] Project directory containing supacloud.config.ts (default: current directory)"),
29795
29723
  include: optional(String2(), "[dev/compile/check] Comma-separated glob patterns for source files"),
29796
29724
  out_dir: optional(String2(), "[dev/compile/plan/build/export-tools] Output directory (default: configured outDir)"),
29797
29725
  strict: optional(Boolean2(), "[dev/compile/check] Promote warnings to errors"),
29798
- format: optional(stringEnum2(["text", "json"]), "[dev/generate/compile/check/plan/release/deploy-plan/diff/history/promote-plan/graph/export-tools] Output format (default: text)"),
29799
- json: optional(Boolean2(), "[history/transfer-plan/transfer/promote-plan] Preserve or return the full machine-readable receipt"),
29726
+ format: optional(stringEnum2(["text", "json"]), "[*] Output format (default: text)"),
29800
29727
  profile: optional(stringEnum2(["fast", "integration"]), "[dev] Run dev or dev:integration; integration verifies an explicit loopback database"),
29801
29728
  once: optional(Boolean2(), "[dev] Validate and report once without watching"),
29802
29729
  watch: optional(Boolean2(), "[dev] Watch for changes (default: true; --once disables)"),
29803
29730
  database_url: optional(String2(), "[dev] Explicit loopback integration URL; watch/once only inspect its shape"),
29804
- "database-url": optional(String2(), "[dev] CLI alias of database_url"),
29805
29731
  target: optional(String2(), "[explain/context/verify-plan] Provider class name / token name / command name / job name / module name"),
29806
29732
  events: optional(String2(), "[context] Project-relative execution metadata JSON or generated JSON-lines file"),
29807
29733
  request_id: optional(String2(), "[context] Opaque request ID or Workflow run ID selected from --events"),
29808
- "request-id": optional(String2(), "[context] CLI alias of request_id"),
29809
29734
  fix: optional(String2(), "[fix] Path to a DiagnosticFix JSON file produced by `doctor --format json`"),
29735
+ from_doctor: optional(String2(), "[fix] Read one fix from a doctor --format json output"),
29736
+ fix_index: optional(Number2({ minimum: 0, multipleOf: 1 }), "[fix] Zero-based fixPlan index used with --from-doctor"),
29810
29737
  write: optional(Boolean2(), "[fix] Write the fix to disk (default: preview only)"),
29811
29738
  skip_build: optional(Boolean2(), "[release] Reuse --manifest_path instead of running the local delivery build")
29812
29739
  }, (request) => runAppTool(request, options));
29813
29740
  }
29814
- var APP_ALIAS_ACTIONS = ["generate", "compile", "check", "graph", "explain", "context", "doctor", "fix"];
29815
- function registerAppAliases(server) {
29816
- const schema = {
29817
- kind: optional(stringEnum2(["module", "command", "query", "controller", "job", "contract", "resource", "feature"]), "[generate] Scaffold kind"),
29818
- name: optional(String2(), "[generate] Object name"),
29819
- module: optional(String2(), "[generate] Target feature module"),
29820
- permission: optional(String2(), "[generate feature] Explicit approval permission; not inferred from name"),
29821
- read_permission: optional(String2(), "[generate feature] Explicit read permission; not inferred from name"),
29822
- "read-permission": optional(String2(), "[generate feature] CLI alias of read_permission"),
29823
- rpc: optional(String2(), "[generate feature] Explicit durable RPC capability; must be registered by the host"),
29824
- database_source: optional(String2(), "[generate feature] Project-relative database source path"),
29825
- database_table: optional(String2(), "[generate feature] Schema-qualified table owned by the feature"),
29826
- database_type: optional(String2(), "[generate feature] Exported row type derived from the database source"),
29827
- "database-source": optional(String2(), "[generate feature] CLI alias of database_source"),
29828
- "database-table": optional(String2(), "[generate feature] CLI alias of database_table"),
29829
- "database-type": optional(String2(), "[generate feature] CLI alias of database_type"),
29830
- dir: optional(String2(), "[generate] Feature root directory (default: src/features)"),
29831
- force: optional(Boolean2(), "[generate] Overwrite a single regular file; resource/feature/controller never overwrite"),
29832
- dry_run: optional(Boolean2(), "[generate] Validate and preview the complete change set without writing"),
29833
- "dry-run": optional(Boolean2(), "[generate] CLI alias of dry_run"),
29834
- register_in: optional(String2(), "[generate] Existing parent module file for module/resource/feature; uses the compiler AST editor"),
29835
- "register-in": optional(String2(), "[generate] CLI alias of register_in"),
29836
- root: optional(String2(), "Project directory containing supacloud.config.ts (default: current directory)"),
29837
- include: optional(String2(), "[compile/check] Comma-separated glob patterns for source files"),
29838
- out_dir: optional(String2(), "[compile/export-tools] Output directory (default: <root>/generated)"),
29839
- strict: optional(Boolean2(), "[compile/check] Promote warnings to errors"),
29840
- format: optional(stringEnum2(["text", "json"]), "Output format (default: text)"),
29841
- target: optional(String2(), "[explain/context] Provider class name / token name / command name / job name / module name"),
29842
- events: optional(String2(), "[context] Project-relative execution metadata JSON or generated JSON-lines file"),
29843
- request_id: optional(String2(), "[context] Opaque request ID or Workflow run ID selected from --events"),
29844
- "request-id": optional(String2(), "[context] CLI alias of request_id"),
29845
- fix: optional(String2(), "[fix] Path to a DiagnosticFix JSON file produced by `doctor --format json`"),
29846
- write: optional(Boolean2(), "[fix] Write the fix to disk (default: preview only)")
29847
- };
29848
- for (const action of APP_ALIAS_ACTIONS) {
29849
- registerTool(server, action, `Top-level alias of \`app ${action}\`.`, schema, (request) => runAppTool({ ...request, action }));
29850
- }
29851
- }
29852
29741
 
29853
29742
  // src/shared/tools/db-governance-tools.ts
29854
29743
  import { existsSync as existsSync13 } from "node:fs";
@@ -30291,7 +30180,7 @@ async function runOfficial(root, workdir, args, environment, options) {
30291
30180
  if (code !== 0)
30292
30181
  throw new Error(`Official Supabase candidate generation failed (exit ${code}); no candidate was published. Select a compatible pinned CLI; no fallback engine is used.`);
30293
30182
  }
30294
- async function initializeCandidate(stage, majorVersion = 17) {
30183
+ async function initializeCandidate(stage, majorVersion = 18) {
30295
30184
  if (![15, 16, 17, 18].includes(majorVersion))
30296
30185
  throw new Error("Select a supported PostgreSQL major version (15-18)");
30297
30186
  await mkdir6(join15(stage, "supabase/migrations"), { recursive: true });
@@ -30718,25 +30607,84 @@ async function runModuleCheckLite(args, root, spawn, liteBinary) {
30718
30607
  `);
30719
30608
  return textResult2(output || "lite db check 无输出", result.exitCode !== 0);
30720
30609
  }
30721
- async function runModuleCheckAction(args, root, environment, spawn, liteBinary) {
30722
- if (args.lite)
30723
- return runModuleCheckLite(args, root, spawn, liteBinary);
30724
- const databaseUrl = args.database_url?.trim() || environment.DATABASE_URL?.trim();
30725
- if (!databaseUrl) {
30726
- throw new Error("db module_check requires --database_url、环境变量 DATABASE_URL 或 --lite");
30727
- }
30728
- const modules = await loadDatabaseModules(root, args.module_file);
30729
- const schemas = parseSchemas(args.schema);
30730
- const client = new Bun.SQL(databaseUrl);
30731
- const executor = {
30732
- async query(sql, params) {
30733
- return await client.unsafe(sql, params ?? []);
30734
- }
30735
- };
30736
- try {
30737
- const reports = [];
30738
- for (const module of modules) {
30739
- reports.push(await runModuleCheck(module, executor, schemas));
30610
+ function remoteModuleManifest(modules) {
30611
+ return modules.map((module) => ({
30612
+ name: module.name,
30613
+ tables: module.tables,
30614
+ policies: module.policies.map((policy) => ({
30615
+ name: policy.name,
30616
+ table: policy.table,
30617
+ operation: policy.operation,
30618
+ roles: policy.roles,
30619
+ ...policy.usingExpr === undefined ? {} : { usingExpr: policy.usingExpr },
30620
+ ...policy.checkExpr === undefined ? {} : { checkExpr: policy.checkExpr }
30621
+ })),
30622
+ functions: module.functions.map((fn) => ({
30623
+ name: fn.name,
30624
+ security: fn.security,
30625
+ ...fn.signature === undefined ? {} : { signature: fn.signature }
30626
+ })),
30627
+ triggers: module.triggers.map((trigger) => ({
30628
+ name: trigger.name,
30629
+ table: trigger.table,
30630
+ ...trigger.enabled === undefined ? {} : { enabled: trigger.enabled }
30631
+ })),
30632
+ grants: module.grants.map((grant) => ({
30633
+ object: grant.object,
30634
+ privilege: grant.privilege,
30635
+ role: grant.role
30636
+ }))
30637
+ }));
30638
+ }
30639
+ async function runModuleCheckRemote(args, root, options) {
30640
+ const run = options.runDatabase?.();
30641
+ if (!run)
30642
+ throw new Error("db module_check --remote 需要 Management API context(SUPACLOUD_API_URL + SUPACLOUD_API_TOKEN)");
30643
+ const ref = args.ref?.trim() || options.projectRef?.trim();
30644
+ if (!ref)
30645
+ throw new Error("db module_check --remote 需要 --ref 或 SUPACLOUD_PROJECT_REF");
30646
+ const modules = await loadDatabaseModules(root, args.module_file);
30647
+ const schemas = args.schema?.split(",").map((entry) => entry.trim()).filter(Boolean) ?? ["public"];
30648
+ return await run({
30649
+ action: "governance_check",
30650
+ ref,
30651
+ modules: remoteModuleManifest(modules),
30652
+ schemas
30653
+ });
30654
+ }
30655
+ async function runRoleCheckRemote(args, options) {
30656
+ const run = options.runDatabase?.();
30657
+ if (!run)
30658
+ throw new Error("db role_check --remote 需要 Management API context(SUPACLOUD_API_URL + SUPACLOUD_API_TOKEN)");
30659
+ const ref = args.ref?.trim() || options.projectRef?.trim();
30660
+ if (!ref)
30661
+ throw new Error("db role_check --remote 需要 --ref 或 SUPACLOUD_PROJECT_REF");
30662
+ const schemas = args.schema?.split(",").map((entry) => entry.trim()).filter(Boolean) ?? ["public"];
30663
+ return await run({ action: "role_check", ref, schemas });
30664
+ }
30665
+ async function runModuleCheckAction(args, root, environment, spawn, liteBinary, options) {
30666
+ if (args.lite && args.remote)
30667
+ throw new Error("db module_check 只能选择 --lite 或 --remote");
30668
+ if (args.remote)
30669
+ return runModuleCheckRemote(args, root, options);
30670
+ if (args.lite)
30671
+ return runModuleCheckLite(args, root, spawn, liteBinary);
30672
+ const databaseUrl = args.database_url?.trim() || environment.DATABASE_URL?.trim();
30673
+ if (!databaseUrl) {
30674
+ throw new Error("db module_check requires --database_url、环境变量 DATABASE_URL、--lite 或 --remote");
30675
+ }
30676
+ const modules = await loadDatabaseModules(root, args.module_file);
30677
+ const schemas = parseSchemas(args.schema);
30678
+ const client = new Bun.SQL(databaseUrl);
30679
+ const executor = {
30680
+ async query(sql, params) {
30681
+ return await client.unsafe(sql, params ?? []);
30682
+ }
30683
+ };
30684
+ try {
30685
+ const reports = [];
30686
+ for (const module of modules) {
30687
+ reports.push(await runModuleCheck(module, executor, schemas));
30740
30688
  }
30741
30689
  const hasError = reports.some((report) => !report.ok);
30742
30690
  return textResult2(reports.map(formatReconcileReport).join(`
@@ -30761,12 +30709,13 @@ function registerDbGovernanceTools(server, options = {}) {
30761
30709
  database_url: optional(String2(), "[module_check] Postgres connection URL (default: DATABASE_URL)"),
30762
30710
  schema: optional(String2(), "[module_check] Comma-separated schemas to inspect (default: public)"),
30763
30711
  lite: optional(Boolean2(), "[module_check] Reconcile against a local SupaCloud Lite project (runs supacloud-lite db check)"),
30712
+ remote: optional(Boolean2(), "[module_check/role_check] Use Management API without a local database URL"),
30764
30713
  project_dir: optional(String2(), "[module_check] Lite project directory (with --lite; default: --root)"),
30765
30714
  schema_dir: optional(String2(), "[diff] Maintained declarative SQL directory (default: supabase/schemas)"),
30766
- db_major_version: optional(Number2(), "[reverse/diff] PostgreSQL shadow major version (default: 17)"),
30715
+ db_major_version: optional(Number2(), "[reverse/diff] PostgreSQL shadow major version (default: 18); set only when matching a non-18 source"),
30767
30716
  out: optional(String2(), "[reverse/diff] Candidate output path (reverse requires a new directory)"),
30768
30717
  dir: optional(String2(), "[diff/plan/apply] Reviewed forward migration directory"),
30769
- ref: optional(String2(), "[plan/apply] Target project ref"),
30718
+ ref: optional(String2(), "[module_check/role_check/plan/apply] Target project ref (remote checks default to SUPACLOUD_PROJECT_REF)"),
30770
30719
  approved_digest: optional(String2(), "[apply] Exact digest returned by db plan"),
30771
30720
  application_role: optional(String2(), "[role_sql] Application database role"),
30772
30721
  migration_role: optional(String2(), "[role_sql] Privileged migration role"),
@@ -30781,12 +30730,20 @@ function registerDbGovernanceTools(server, options = {}) {
30781
30730
  case "explain":
30782
30731
  return runExplain2(request, root);
30783
30732
  case "module_check":
30784
- return runModuleCheckAction(request, root, environment, liteSpawn, options.liteBinary);
30733
+ return runModuleCheckAction(request, root, environment, liteSpawn, options.liteBinary, options);
30734
+ case "role_check":
30735
+ if (request.remote)
30736
+ return runRoleCheckRemote(request, options);
30737
+ return runDbWorkflow({ ...request, action: request.action }, root, {
30738
+ environment,
30739
+ projectRef: options.projectRef,
30740
+ apiUrl: options.apiUrl,
30741
+ runDatabase: options.runDatabase
30742
+ });
30785
30743
  case "reverse":
30786
30744
  case "diff":
30787
30745
  case "plan":
30788
30746
  case "apply":
30789
- case "role_check":
30790
30747
  case "role_sql":
30791
30748
  return runDbWorkflow({ ...request, action: request.action }, root, {
30792
30749
  environment,
@@ -32126,11 +32083,11 @@ function registerReleaseTools(server, http, options = {}) {
32126
32083
  }
32127
32084
 
32128
32085
  // src/shared/tools/deploy-tools.ts
32129
- import { access, lstat as lstat5, mkdtemp as mkdtemp2, opendir, readFile as readFile6, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
32086
+ import { access, lstat as lstat5, mkdtemp as mkdtemp2, opendir, readFile as readFile7, rm as rm3, writeFile as writeFile7 } from "node:fs/promises";
32130
32087
  import { spawn as spawn2 } from "node:child_process";
32131
32088
  import { createHash as createHash11 } from "node:crypto";
32132
32089
  import { tmpdir as tmpdir3 } from "node:os";
32133
- import { basename as basename8, dirname as dirname13, join as join18, relative as relative10, resolve as resolve24, sep as sep5 } from "node:path";
32090
+ import { basename as basename8, dirname as dirname14, join as join19, relative as relative10, resolve as resolve26, sep as sep5 } from "node:path";
32134
32091
 
32135
32092
  // src/shared/tools/project-deployment.ts
32136
32093
  async function requireProjectDeploymentProtocol(http, projectRef, environmentId) {
@@ -32205,6 +32162,364 @@ async function readProjectDeploymentStatus(http, projectRef, environmentId, depl
32205
32162
  }
32206
32163
  }
32207
32164
 
32165
+ // src/shared/tools/developer-experience-tools.ts
32166
+ import { existsSync as existsSync16 } from "node:fs";
32167
+ import { mkdir as mkdir7, readdir as readdir7, readFile as readFile6, writeFile as writeFile6 } from "node:fs/promises";
32168
+ import { dirname as dirname13, join as join18, resolve as resolve25 } from "node:path";
32169
+
32170
+ // src/shared/context.ts
32171
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
32172
+ import { homedir as homedir3 } from "node:os";
32173
+ import { resolve as resolve24 } from "node:path";
32174
+ var CORE_CONTEXT_KEYS2 = [
32175
+ "SUPABASE_URL",
32176
+ "SUPABASE_SERVICE_ROLE_KEY",
32177
+ "SUPACLOUD_API_URL",
32178
+ "SUPACLOUD_API_TOKEN",
32179
+ "SUPACLOUD_PROJECT_REF",
32180
+ "SUPACLOUD_HOST",
32181
+ "SUPACLOUD_APPLICATION_ID",
32182
+ "SUPACLOUD_APPLICATION_ENVIRONMENT_ID",
32183
+ "SUPACLOUD_APPLICATION_CONFIGURATION_PATH",
32184
+ "SUPACLOUD_APPLICATION_CONFIGURATION_ID",
32185
+ "SUPACLOUD_APPLICATION_MANIFEST_PATH"
32186
+ ];
32187
+ function unquotedEnvValue2(rawValue) {
32188
+ const value = rawValue.trim();
32189
+ const quote = value[0];
32190
+ return quote && (quote === '"' || quote === "'") && value.endsWith(quote) ? value.slice(1, -1) : value;
32191
+ }
32192
+ function parseEnvFile2(contents) {
32193
+ const values = {};
32194
+ for (const rawLine of contents.split(/\r?\n/)) {
32195
+ const line = rawLine.trim();
32196
+ if (!line || line.startsWith("#"))
32197
+ continue;
32198
+ const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
32199
+ if (!match)
32200
+ continue;
32201
+ values[match[1]] = unquotedEnvValue2(match[2]);
32202
+ }
32203
+ return values;
32204
+ }
32205
+ function readEnvFile2(path, required) {
32206
+ if (!existsSync15(path)) {
32207
+ if (required)
32208
+ throw new Error(`SupaCloud environment file not found: ${path}`);
32209
+ return {};
32210
+ }
32211
+ try {
32212
+ return parseEnvFile2(readFileSync12(path, "utf8"));
32213
+ } catch (error) {
32214
+ const message = error instanceof Error ? error.message : String(error);
32215
+ throw new Error(`Failed to read SupaCloud environment file ${path}: ${message}`);
32216
+ }
32217
+ }
32218
+ function canonicalApiOrigin2(value) {
32219
+ const candidate = value.trim();
32220
+ if (!candidate)
32221
+ return "";
32222
+ try {
32223
+ const url = new URL(candidate);
32224
+ const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
32225
+ const allowedProtocol = url.protocol === "https:" || url.protocol === "http:" && loopback;
32226
+ const exactOrigin = candidate === url.origin || candidate === `${url.origin}/`;
32227
+ return allowedProtocol && !url.username && !url.password && exactOrigin && url.pathname === "/" && !url.search && !url.hash ? url.origin : "";
32228
+ } catch (error) {
32229
+ if (error instanceof TypeError)
32230
+ return "";
32231
+ throw error;
32232
+ }
32233
+ }
32234
+ function hostFromUrl2(value) {
32235
+ return value ? new URL(value).hostname : "";
32236
+ }
32237
+ function inferProjectRefFromSupabaseUrl2(value) {
32238
+ if (!value)
32239
+ return "";
32240
+ return new URL(value).hostname.match(/^([a-z0-9-]+)\.api\./i)?.[1] ?? "";
32241
+ }
32242
+ function sourceCredentialScope2(values, explicitApiUrl, supabaseUrl) {
32243
+ const hasManagementContext = Boolean(explicitApiUrl.trim() || values.SUPACLOUD_API_TOKEN?.trim() || values.SUPACLOUD_HOST?.trim());
32244
+ if (hasManagementContext)
32245
+ return "management";
32246
+ return supabaseUrl || values.SUPABASE_SERVICE_ROLE_KEY?.trim() ? "project_application" : "incomplete";
32247
+ }
32248
+ function sourceProjectCore2(values) {
32249
+ const supabaseUrl = canonicalApiOrigin2(values.SUPABASE_URL || "");
32250
+ const projectRef = (values.SUPACLOUD_PROJECT_REF || "").trim() || inferProjectRefFromSupabaseUrl2(supabaseUrl);
32251
+ const explicitApiUrl = values.SUPACLOUD_API_URL || "";
32252
+ const credentialScope = sourceCredentialScope2(values, explicitApiUrl, supabaseUrl);
32253
+ const managementUrl = explicitApiUrl || (values.SUPACLOUD_HOST ? `http://${values.SUPACLOUD_HOST}:9090` : "");
32254
+ const apiUrl = credentialScope === "management" ? canonicalApiOrigin2(managementUrl) : "";
32255
+ const apiToken = credentialScope === "management" ? values.SUPACLOUD_API_TOKEN || "" : "";
32256
+ return { apiUrl, apiToken, projectRef, supabaseUrl, credentialScope };
32257
+ }
32258
+ function processValues2(env) {
32259
+ return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== undefined));
32260
+ }
32261
+ function hasProcessContext2(env) {
32262
+ return CORE_CONTEXT_KEYS2.some((key) => Object.hasOwn(env, key));
32263
+ }
32264
+ function completeProjectContext2(values) {
32265
+ const core = sourceProjectCore2(values);
32266
+ if (core.credentialScope === "project_application") {
32267
+ return Boolean(core.supabaseUrl && values.SUPABASE_SERVICE_ROLE_KEY?.trim() && core.projectRef);
32268
+ }
32269
+ return Boolean(core.apiUrl && core.apiToken && core.projectRef);
32270
+ }
32271
+ function parseInsecureTls2(values) {
32272
+ const value = values.SUPACLOUD_TLS_VERIFY?.trim().toLowerCase();
32273
+ if (value === undefined)
32274
+ return true;
32275
+ if (["1", "true", "yes", "on"].includes(value))
32276
+ return false;
32277
+ if (["0", "false", "no", "off"].includes(value))
32278
+ return true;
32279
+ throw new Error("SUPACLOUD_TLS_VERIFY must be a boolean (true or false)");
32280
+ }
32281
+ function namedEnvironmentSource2(cwd, selector) {
32282
+ const environment = normalizeEnvironmentName(selector);
32283
+ const path = resolve24(cwd, `.env.supacloud.${selector}`);
32284
+ const values = readEnvFile2(path, true);
32285
+ if (values.SUPACLOUD_ENV && normalizeEnvironmentName(values.SUPACLOUD_ENV) !== environment) {
32286
+ throw new Error(`SUPACLOUD_ENV in ${path} does not match selector ${selector}`);
32287
+ }
32288
+ return { values, kind: "named_env_file", path, environment };
32289
+ }
32290
+ function explicitEnvironmentSource2(cwd, envFile) {
32291
+ const path = resolve24(cwd, envFile);
32292
+ const values = readEnvFile2(path, true);
32293
+ if (!values.SUPACLOUD_ENV)
32294
+ throw new Error(`SUPACLOUD_ENV is required in ${path}`);
32295
+ return {
32296
+ values,
32297
+ kind: "explicit_env_file",
32298
+ path,
32299
+ environment: normalizeEnvironmentName(values.SUPACLOUD_ENV)
32300
+ };
32301
+ }
32302
+ function contextSource2(env, cwd, selection) {
32303
+ if (selection.environmentName && selection.envFile)
32304
+ throw new Error("Environment selectors are mutually exclusive");
32305
+ if (selection.environmentName)
32306
+ return namedEnvironmentSource2(cwd, selection.environmentName);
32307
+ if (selection.envFile)
32308
+ return explicitEnvironmentSource2(cwd, selection.envFile);
32309
+ if (env.SUPACLOUD_ENV) {
32310
+ const environment = normalizeEnvironmentName(env.SUPACLOUD_ENV);
32311
+ const values = processValues2(env);
32312
+ if (completeProjectContext2(values)) {
32313
+ return { values, kind: "process_env", path: null, environment };
32314
+ }
32315
+ return namedEnvironmentSource2(cwd, env.SUPACLOUD_ENV);
32316
+ }
32317
+ if (hasProcessContext2(env)) {
32318
+ return { values: processValues2(env), kind: "process_env", path: null, environment: "" };
32319
+ }
32320
+ const path = resolve24(cwd, ".env");
32321
+ const values = readEnvFile2(path, false);
32322
+ const environment = values.SUPACLOUD_ENV ? normalizeEnvironmentName(values.SUPACLOUD_ENV) : "";
32323
+ return { values, kind: Object.keys(values).length ? "legacy_dotenv" : "none", path: existsSync15(path) ? path : null, environment };
32324
+ }
32325
+ function resolveSupaCloudContext2(env = process.env, cwd = process.cwd(), selection = {}) {
32326
+ const source = contextSource2(env, cwd, selection);
32327
+ const core = sourceProjectCore2(source.values);
32328
+ const host = source.values.SUPACLOUD_HOST || hostFromUrl2(core.apiUrl || core.supabaseUrl);
32329
+ const readOnly = env.SUPACLOUD_READ_ONLY === "true" || source.values.SUPACLOUD_READ_ONLY === "true";
32330
+ const insecureTls = parseInsecureTls2(source.values);
32331
+ return {
32332
+ host,
32333
+ sshUser: env.SUPACLOUD_SSH_USER ?? "root",
32334
+ sshPort: parseInt(env.SUPACLOUD_SSH_PORT ?? "22", 10),
32335
+ sshKey: env.SUPACLOUD_SSH_KEY ?? resolve24(homedir3(), ".ssh", "id_rsa"),
32336
+ sshPass: env.SUPACLOUD_SSH_PASS ?? "",
32337
+ apiUrl: core.apiUrl,
32338
+ apiToken: core.apiToken,
32339
+ projectRef: core.projectRef,
32340
+ readOnly,
32341
+ environment: source.environment,
32342
+ production: source.environment === "prod" || source.environment === "production",
32343
+ inferredSupabaseUrl: core.supabaseUrl,
32344
+ inferredServiceRoleKey: source.values.SUPABASE_SERVICE_ROLE_KEY || "",
32345
+ credentialScope: core.credentialScope,
32346
+ source: source.kind,
32347
+ sourcePath: source.path,
32348
+ environmentVariables: source.values,
32349
+ applicationId: source.values.SUPACLOUD_APPLICATION_ID?.trim() || "",
32350
+ applicationEnvironmentId: source.values.SUPACLOUD_APPLICATION_ENVIRONMENT_ID?.trim() || source.environment,
32351
+ applicationConfigurationPath: source.values.SUPACLOUD_APPLICATION_CONFIGURATION_PATH?.trim() || "",
32352
+ applicationConfigurationId: source.values.SUPACLOUD_APPLICATION_CONFIGURATION_ID?.trim() || "",
32353
+ applicationManifestPath: source.values.SUPACLOUD_APPLICATION_MANIFEST_PATH?.trim() || "",
32354
+ insecureTls
32355
+ };
32356
+ }
32357
+
32358
+ // src/shared/tools/developer-experience-tools.ts
32359
+ var initToolSchema = {
32360
+ root: optional(String2(), "Project directory to initialize (default: current directory)"),
32361
+ format: optional(stringEnum2(["text", "json"]), "Output format (default: text)")
32362
+ };
32363
+ var environmentToolSchema = {
32364
+ action: withDescription(stringEnum2(["list", "check"]), "Environment profile inspection action"),
32365
+ root: optional(String2(), "Project directory containing .env.supacloud.* files"),
32366
+ format: optional(stringEnum2(["text", "json"]), "Output format (default: text)")
32367
+ };
32368
+ var TEST_PROFILE = [
32369
+ "SUPACLOUD_ENV=test",
32370
+ "SUPACLOUD_API_URL=https://management.test.example.com",
32371
+ "SUPACLOUD_API_TOKEN=<test-management-api-token>",
32372
+ "SUPACLOUD_PROJECT_REF=<test-project-ref>",
32373
+ "# Optional for executable application delivery: SUPACLOUD_APPLICATION_ID=<application-id>",
32374
+ ""
32375
+ ].join(`
32376
+ `);
32377
+ var PRODUCTION_PROFILE = [
32378
+ "SUPACLOUD_ENV=production",
32379
+ "SUPACLOUD_API_URL=https://management.example.com",
32380
+ "SUPACLOUD_API_TOKEN=<production-management-api-token>",
32381
+ "SUPACLOUD_PROJECT_REF=<production-project-ref>",
32382
+ "# Optional for executable application delivery: SUPACLOUD_APPLICATION_ID=<application-id>",
32383
+ ""
32384
+ ].join(`
32385
+ `);
32386
+ function textResult3(text, isError = false) {
32387
+ return { isError, content: [{ type: "text", text }] };
32388
+ }
32389
+ function jsonResult(value, isError = false) {
32390
+ return textResult3(JSON.stringify(value, null, 2), isError);
32391
+ }
32392
+ function record5(value) {
32393
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
32394
+ }
32395
+ function packageDependencies(manifest) {
32396
+ return new Set([
32397
+ ...Object.keys(manifest.dependencies ?? {}),
32398
+ ...Object.keys(manifest.devDependencies ?? {})
32399
+ ]);
32400
+ }
32401
+ function outputDirectoryFor(manifest) {
32402
+ const dependencies = packageDependencies(manifest);
32403
+ if (dependencies.has("next"))
32404
+ return "out";
32405
+ if (dependencies.has("@sveltejs/kit") || dependencies.has("svelte"))
32406
+ return "build";
32407
+ return "dist";
32408
+ }
32409
+ async function readPackageManifest(root) {
32410
+ const path = join18(root, "package.json");
32411
+ if (!existsSync16(path))
32412
+ return {};
32413
+ try {
32414
+ const parsed = JSON.parse(await readFile6(path, "utf8"));
32415
+ const value = record5(parsed);
32416
+ if (!value)
32417
+ throw new Error("package.json must contain an object");
32418
+ const scripts = record5(value.scripts);
32419
+ const dependencies = record5(value.dependencies);
32420
+ const devDependencies = record5(value.devDependencies);
32421
+ return {
32422
+ scripts: scripts ?? undefined,
32423
+ dependencies: dependencies ?? undefined,
32424
+ devDependencies: devDependencies ?? undefined
32425
+ };
32426
+ } catch (error) {
32427
+ throw new Error(`Invalid package.json: ${error instanceof Error ? error.message : String(error)}`);
32428
+ }
32429
+ }
32430
+ async function writeNewFile(path, content) {
32431
+ try {
32432
+ await writeFile6(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
32433
+ return true;
32434
+ } catch (error) {
32435
+ if (error instanceof Error && "code" in error && error.code === "EEXIST")
32436
+ return false;
32437
+ throw error;
32438
+ }
32439
+ }
32440
+ async function ensureGitignore(root) {
32441
+ const path = join18(root, ".gitignore");
32442
+ const existing = existsSync16(path) ? await readFile6(path, "utf8") : "";
32443
+ const required = [".env.supacloud.*", "!.env.supacloud.*.example"];
32444
+ const missing = required.filter((entry) => !existing.split(/\r?\n/).includes(entry));
32445
+ if (missing.length === 0)
32446
+ return false;
32447
+ const prefix = existing.length > 0 && !existing.endsWith(`
32448
+ `) ? `${existing}
32449
+ ` : existing;
32450
+ await writeFile6(path, `${prefix}${missing.join(`
32451
+ `)}
32452
+ `, "utf8");
32453
+ return true;
32454
+ }
32455
+ async function initializeProject(request, options = {}) {
32456
+ const root = resolve25(request.root || options.cwd || process.cwd());
32457
+ await mkdir7(root, { recursive: true });
32458
+ const configPath = join18(root, "supacloud.json");
32459
+ if (existsSync16(configPath)) {
32460
+ throw new Error(`supacloud.json already exists in ${root}; init never overwrites project configuration`);
32461
+ }
32462
+ const manifest = await readPackageManifest(root);
32463
+ const hasBuildScript = typeof manifest.scripts?.build === "string" && Boolean(manifest.scripts.build.trim());
32464
+ const outputDirectory = outputDirectoryFor(manifest);
32465
+ const config = {
32466
+ frontend: {
32467
+ root: ".",
32468
+ outputDirectory
32469
+ }
32470
+ };
32471
+ const created = [];
32472
+ try {
32473
+ await writeFile6(configPath, `${JSON.stringify(config, null, 2)}
32474
+ `, {
32475
+ encoding: "utf8",
32476
+ flag: "wx",
32477
+ mode: 420
32478
+ });
32479
+ } catch (error) {
32480
+ if (error instanceof Error && "code" in error && error.code === "EEXIST") {
32481
+ throw new Error(`supacloud.json already exists in ${root}; init never overwrites project configuration`);
32482
+ }
32483
+ throw error;
32484
+ }
32485
+ created.push("supacloud.json");
32486
+ if (await writeNewFile(join18(root, ".env.supacloud.test.example"), TEST_PROFILE)) {
32487
+ created.push(".env.supacloud.test.example");
32488
+ }
32489
+ if (await writeNewFile(join18(root, ".env.supacloud.production.example"), PRODUCTION_PROFILE)) {
32490
+ created.push(".env.supacloud.production.example");
32491
+ }
32492
+ if (await ensureGitignore(root))
32493
+ created.push(".gitignore");
32494
+ const result = {
32495
+ schema: "supacloud.developer-experience.init.v1",
32496
+ ok: true,
32497
+ root,
32498
+ created,
32499
+ detected: {
32500
+ package_json: existsSync16(join18(root, "package.json")),
32501
+ build_script: hasBuildScript,
32502
+ output_directory: outputDirectory
32503
+ },
32504
+ next: [
32505
+ "cp .env.supacloud.test.example .env.supacloud.test",
32506
+ "Configure frontend assets; set SUPACLOUD_APPLICATION_ID only for executable application delivery.",
32507
+ "supacloud env check",
32508
+ "supacloud --env test deploy"
32509
+ ]
32510
+ };
32511
+ if (request.format === "json")
32512
+ return jsonResult(result);
32513
+ return textResult3([
32514
+ `Initialized SupaCloud deployment in ${root}.`,
32515
+ `Created: ${created.join(", ")}.`,
32516
+ hasBuildScript ? `Detected package.json build script; deploy output defaults to ${outputDirectory}.` : "No package.json build script detected; add one before deploying.",
32517
+ "",
32518
+ ...result.next
32519
+ ].join(`
32520
+ `));
32521
+ }
32522
+
32208
32523
  // src/shared/tools/deploy-tools.ts
32209
32524
  var MAX_SOURCE_BYTES = 256 * 1024 * 1024;
32210
32525
  var PUBLIC_BUILD_ENV_PREFIXES = ["PUBLIC_", "VITE_", "NEXT_PUBLIC_", "NUXT_PUBLIC_", "ASTRO_PUBLIC_"];
@@ -32213,18 +32528,21 @@ function isPublicBuildEnv(name) {
32213
32528
  return PUBLIC_BUILD_ENV_PREFIXES.some((prefix) => name.startsWith(prefix));
32214
32529
  }
32215
32530
  function isProtectedBuildEnv(name) {
32216
- return name.startsWith("SUPACLOUD_") || name === "SUPABASE_SERVICE_ROLE_KEY" || name === "SUPABASE_ACCESS_TOKEN" || name === "MANAGEMENT_API_URL" || name === "MANAGEMENT_API_TOKEN";
32531
+ return name.startsWith("SUPACLOUD_") || name === "SUPABASE_SERVICE_ROLE_KEY" || name === "SUPABASE_ACCESS_TOKEN" || name === "MANAGEMENT_API_TOKEN";
32217
32532
  }
32218
32533
  var deployToolSchema = {
32534
+ action: optional(stringEnum2(["init"]), "[init] Create local deployment configuration and environment examples"),
32535
+ ...initToolSchema,
32536
+ root: optional(String2(), "[init] Project directory to initialize (default: current directory)"),
32219
32537
  ref: optional(String2(), "Project ref (defaults to the linked project)"),
32220
32538
  cwd: optional(String2(), "Project directory (default: current directory)"),
32221
32539
  skip_build: optional(Boolean2(), "Use an existing output directory without building"),
32222
32540
  assets_only: optional(Boolean2(), "Publish only configured static assets; leave backend Functions, schedules and migrations unchanged"),
32223
32541
  backend: optional(Boolean2(), "Publish the application backend independently; do not build or activate frontend assets"),
32224
32542
  status: optional(String2({ format: "uuid" }), "Read a backend deployment status without publishing"),
32225
- json: optional(Boolean2(), "Print only the final JSON result")
32543
+ format: optional(stringEnum2(["text", "json"]), "Output format (default: text)")
32226
32544
  };
32227
- function record5(candidate) {
32545
+ function record6(candidate) {
32228
32546
  return candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
32229
32547
  }
32230
32548
  function requiredString(candidate, field) {
@@ -32234,7 +32552,7 @@ function requiredString(candidate, field) {
32234
32552
  return candidate.trim();
32235
32553
  }
32236
32554
  function frontendDeployment(candidate) {
32237
- const value = record5(candidate);
32555
+ const value = record6(candidate);
32238
32556
  if (!value)
32239
32557
  throw new Error("Invalid frontend deployment response");
32240
32558
  return {
@@ -32248,7 +32566,7 @@ function frontendDeployment(candidate) {
32248
32566
  }
32249
32567
  async function readJson(path) {
32250
32568
  try {
32251
- return JSON.parse(await readFile6(path, "utf8"));
32569
+ return JSON.parse(await readFile7(path, "utf8"));
32252
32570
  } catch (error) {
32253
32571
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
32254
32572
  return null;
@@ -32256,15 +32574,15 @@ async function readJson(path) {
32256
32574
  }
32257
32575
  }
32258
32576
  async function findDeployConfigRoot(startDirectory, environmentName) {
32259
- let directory = resolve24(startDirectory);
32577
+ let directory = resolve26(startDirectory);
32260
32578
  while (true) {
32261
32579
  const environmentFile = environmentName ? `.env.supacloud.${environmentName}` : null;
32262
- if (await pathExists(join18(directory, "supacloud.json")) || await pathExists(join18(directory, "supacloud.config.ts")) || environmentFile !== null && await pathExists(join18(directory, environmentFile))) {
32580
+ if (await pathExists(join19(directory, "supacloud.json")) || await pathExists(join19(directory, "supacloud.config.ts")) || environmentFile !== null && await pathExists(join19(directory, environmentFile))) {
32263
32581
  return directory;
32264
32582
  }
32265
- const parent = dirname13(directory);
32583
+ const parent = dirname14(directory);
32266
32584
  if (parent === directory)
32267
- return resolve24(startDirectory);
32585
+ return resolve26(startDirectory);
32268
32586
  directory = parent;
32269
32587
  }
32270
32588
  }
@@ -32304,7 +32622,7 @@ function optionalStringArray(value, field) {
32304
32622
  });
32305
32623
  }
32306
32624
  function deployTargetConfig(candidate, name) {
32307
- const value = record5(candidate);
32625
+ const value = record6(candidate);
32308
32626
  if (!value || value.type !== "frontend" && value.type !== "edge_function") {
32309
32627
  throw new Error(`supacloud.json targets.${name}.type must be 'frontend' or 'edge_function'`);
32310
32628
  }
@@ -32329,14 +32647,14 @@ function deployTargetConfig(candidate, name) {
32329
32647
  };
32330
32648
  }
32331
32649
  async function readDeployConfig(configRoot) {
32332
- const configPath = join18(configRoot, "supacloud.json");
32650
+ const configPath = join19(configRoot, "supacloud.json");
32333
32651
  const candidate = await readJson(configPath);
32334
32652
  if (candidate === null)
32335
32653
  return {};
32336
- const config = record5(candidate);
32337
- const frontend = record5(config?.frontend);
32338
- const application = record5(config?.application);
32339
- const targetValues = record5(config?.targets);
32654
+ const config = record6(candidate);
32655
+ const frontend = record6(config?.frontend);
32656
+ const application = record6(config?.application);
32657
+ const targetValues = record6(config?.targets);
32340
32658
  if (!config || config.frontend !== undefined && !frontend || config.application !== undefined && !application || config.targets !== undefined && !targetValues) {
32341
32659
  throw new Error("supacloud.json must contain object-valued 'application', 'frontend' or 'targets' configuration");
32342
32660
  }
@@ -32376,16 +32694,16 @@ async function readDeployConfig(configRoot) {
32376
32694
  };
32377
32695
  }
32378
32696
  async function packageName(projectDirectory) {
32379
- const candidate = record5(await readJson(join18(projectDirectory, "package.json")));
32697
+ const candidate = record6(await readJson(join19(projectDirectory, "package.json")));
32380
32698
  return typeof candidate?.name === "string" && candidate.name.trim() ? candidate.name.trim() : null;
32381
32699
  }
32382
32700
  async function pathExists(path) {
32383
32701
  return access(path).then(() => true, () => false);
32384
32702
  }
32385
32703
  async function hasProjectReleaseManifest(root) {
32386
- if (await pathExists(join18(root, "deploy-manifest.json")))
32704
+ if (await pathExists(join19(root, "deploy-manifest.json")))
32387
32705
  return true;
32388
- const directory = await opendir(join18(root, "supacloud")).catch((error) => {
32706
+ const directory = await opendir(join19(root, "supacloud")).catch((error) => {
32389
32707
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
32390
32708
  return null;
32391
32709
  throw error;
@@ -32396,28 +32714,28 @@ async function hasProjectReleaseManifest(root) {
32396
32714
  for await (const entry of directory) {
32397
32715
  if (++entries > 128)
32398
32716
  throw new Error("Too many SupaCloud project roots to resolve a deployment");
32399
- if ((entry.isDirectory() || entry.isSymbolicLink()) && await pathExists(join18(root, "supacloud", entry.name, "deploy-manifest.json")))
32717
+ if ((entry.isDirectory() || entry.isSymbolicLink()) && await pathExists(join19(root, "supacloud", entry.name, "deploy-manifest.json")))
32400
32718
  return true;
32401
32719
  }
32402
32720
  return false;
32403
32721
  }
32404
32722
  async function defaultBuildCommand(projectDirectory, configRoot) {
32405
- const packageJson = record5(await readJson(join18(projectDirectory, "package.json")));
32406
- const scripts = record5(packageJson?.scripts);
32723
+ const packageJson = record6(await readJson(join19(projectDirectory, "package.json")));
32724
+ const scripts = record6(packageJson?.scripts);
32407
32725
  if (typeof scripts?.build !== "string" || !scripts.build.trim()) {
32408
32726
  throw new Error("No build command was configured and package.json has no build script");
32409
32727
  }
32410
32728
  let directory = projectDirectory;
32411
32729
  while (true) {
32412
- if (await pathExists(join18(directory, "bun.lock")) || await pathExists(join18(directory, "bun.lockb")))
32730
+ if (await pathExists(join19(directory, "bun.lock")) || await pathExists(join19(directory, "bun.lockb")))
32413
32731
  return "bun run build";
32414
- if (await pathExists(join18(directory, "pnpm-lock.yaml")))
32732
+ if (await pathExists(join19(directory, "pnpm-lock.yaml")))
32415
32733
  return "pnpm run build";
32416
- if (await pathExists(join18(directory, "yarn.lock")))
32734
+ if (await pathExists(join19(directory, "yarn.lock")))
32417
32735
  return "yarn build";
32418
32736
  if (directory === configRoot)
32419
32737
  break;
32420
- const parent = dirname13(directory);
32738
+ const parent = dirname14(directory);
32421
32739
  if (parent === directory || !directory.startsWith(`${configRoot}${sep5}`))
32422
32740
  break;
32423
32741
  directory = parent;
@@ -32425,7 +32743,7 @@ async function defaultBuildCommand(projectDirectory, configRoot) {
32425
32743
  return "npm run build";
32426
32744
  }
32427
32745
  function deploymentList(candidate) {
32428
- const values = Array.isArray(candidate) ? candidate : Array.isArray(record5(candidate)?.deployments) ? record5(candidate)?.deployments : null;
32746
+ const values = Array.isArray(candidate) ? candidate : Array.isArray(record6(candidate)?.deployments) ? record6(candidate)?.deployments : null;
32429
32747
  if (!values)
32430
32748
  throw new Error("Invalid frontend deployment list response");
32431
32749
  return values.map(frontendDeployment);
@@ -32485,9 +32803,9 @@ function selectFrontendTarget(configRoot, config) {
32485
32803
  throw new Error(`Multiple frontend deploy targets found (${targets.map(([name]) => name).sort().join(", ")}). Set defaultTarget to the Web target`);
32486
32804
  }
32487
32805
  function targetRoot(configRoot, target, name) {
32488
- const root = resolve24(configRoot, target.root || (name === "frontend" ? "." : name));
32489
- const prefix = `${resolve24(configRoot)}${sep5}`;
32490
- if (root !== resolve24(configRoot) && !root.startsWith(prefix)) {
32806
+ const root = resolve26(configRoot, target.root || (name === "frontend" ? "." : name));
32807
+ const prefix = `${resolve26(configRoot)}${sep5}`;
32808
+ if (root !== resolve26(configRoot) && !root.startsWith(prefix)) {
32491
32809
  throw new Error(`Deploy target '${name}' root must stay inside the repository root`);
32492
32810
  }
32493
32811
  return root;
@@ -32505,8 +32823,8 @@ function defaultOutputDirectory(framework) {
32505
32823
  }
32506
32824
  }
32507
32825
  function resolveInsideProject(projectDirectory, candidate, allowRoot = false) {
32508
- const resolved = resolve24(projectDirectory, candidate);
32509
- const projectRoot = resolve24(projectDirectory);
32826
+ const resolved = resolve26(projectDirectory, candidate);
32827
+ const projectRoot = resolve26(projectDirectory);
32510
32828
  const projectPrefix = `${projectRoot}${sep5}`;
32511
32829
  if (!allowRoot && resolved === projectRoot || resolved !== projectRoot && !resolved.startsWith(projectPrefix)) {
32512
32830
  throw new Error("Frontend output directory must stay inside the project directory");
@@ -32560,7 +32878,7 @@ async function collectArchiveFiles(root) {
32560
32878
  entries.push(entry);
32561
32879
  entries.sort((left, right) => left.name.localeCompare(right.name, "en"));
32562
32880
  for (const entry of entries) {
32563
- const path = join18(directory, entry.name);
32881
+ const path = join19(directory, entry.name);
32564
32882
  const stats = await lstat5(path);
32565
32883
  if (stats.isSymbolicLink())
32566
32884
  throw new Error(`Frontend output cannot contain symbolic links: ${path}`);
@@ -32579,7 +32897,7 @@ async function collectArchiveFiles(root) {
32579
32897
  const files = new Map;
32580
32898
  const digests = [];
32581
32899
  for (const path of paths) {
32582
- const data = new Uint8Array(await readFile6(path));
32900
+ const data = new Uint8Array(await readFile7(path));
32583
32901
  bytes += data.byteLength;
32584
32902
  if (bytes > MAX_SOURCE_BYTES)
32585
32903
  throw new Error(`Frontend output exceeds ${MAX_SOURCE_BYTES} bytes`);
@@ -32598,11 +32916,11 @@ async function collectArchiveFiles(root) {
32598
32916
  return { files, bytes, count: paths.length, treeSha256: tree.digest("hex") };
32599
32917
  }
32600
32918
  async function createCollectedFrontendArchive(outputDirectory, collected) {
32601
- const temporaryDirectory = await mkdtemp2(join18(tmpdir3(), "supacloud-deploy-"));
32602
- const archivePath = join18(temporaryDirectory, `${basename8(outputDirectory)}.tar.zst`);
32919
+ const temporaryDirectory = await mkdtemp2(join19(tmpdir3(), "supacloud-deploy-"));
32920
+ const archivePath = join19(temporaryDirectory, `${basename8(outputDirectory)}.tar.zst`);
32603
32921
  try {
32604
32922
  const archiveBytes = await createFrontendTarZstd(collected.files);
32605
- await writeFile6(archivePath, archiveBytes);
32923
+ await writeFile7(archivePath, archiveBytes);
32606
32924
  const sha256 = createHash11("sha256").update(archiveBytes).digest("hex");
32607
32925
  return {
32608
32926
  archivePath,
@@ -32647,7 +32965,13 @@ async function publishConfiguredFrontend(http, projectRef, configRoot, skipBuild
32647
32965
  }
32648
32966
  function registerDeployTools(server, http, options = {}) {
32649
32967
  registerTool(server, "deploy", "Build and publish the linked project with one command", deployToolSchema, async (args) => {
32650
- const invocationDirectory = resolve24(String(args.cwd || options.cwd || process.cwd()));
32968
+ const invocationDirectory = resolve26(String(args.cwd || options.cwd || process.cwd()));
32969
+ if (args.action === "init") {
32970
+ return initializeProject({
32971
+ ...typeof args.root === "string" ? { root: args.root } : {},
32972
+ ...args.format === "json" ? { format: "json" } : {}
32973
+ }, { cwd: invocationDirectory });
32974
+ }
32651
32975
  const projectRef = String(args.ref || options.projectRef || "").trim();
32652
32976
  if (!projectRef)
32653
32977
  throw new Error("A linked project ref is required. Set SUPACLOUD_PROJECT_REF or pass --ref");
@@ -32678,7 +33002,7 @@ function registerDeployTools(server, http, options = {}) {
32678
33002
  throw new Error("Static asset deployment requires SUPACLOUD_ENV");
32679
33003
  }
32680
33004
  await requireProjectDeploymentProtocol(http, projectRef, environmentId);
32681
- const result = await publishConfiguredFrontend(http, projectRef, configRoot, args.skip_build === true, args.json === true, options.environmentVariables);
33005
+ const result = await publishConfiguredFrontend(http, projectRef, configRoot, args.skip_build === true, args.format === "json", options.environmentVariables);
32682
33006
  if (!result)
32683
33007
  throw new Error("Static asset deployment did not produce a receipt");
32684
33008
  return result;
@@ -32713,7 +33037,7 @@ function registerDeployTools(server, http, options = {}) {
32713
33037
  ...configurationId ? { configuration_id: configurationId } : {},
32714
33038
  ...application.manifestPath ? { manifest_path: application.manifestPath } : {},
32715
33039
  ...args.skip_build === true ? { skip_build: true } : {},
32716
- ...args.json === true ? { format: "json", json: true } : {}
33040
+ ...args.format === "json" ? { format: "json" } : {}
32717
33041
  });
32718
33042
  return result;
32719
33043
  }
@@ -32722,11 +33046,11 @@ function registerDeployTools(server, http, options = {}) {
32722
33046
  }
32723
33047
 
32724
33048
  // src/shared/tools/remote-dev-tools.ts
32725
- import { existsSync as existsSync15 } from "node:fs";
32726
- import { readFile as readFile7 } from "node:fs/promises";
32727
- import { readdir as readdir7 } from "node:fs/promises";
33049
+ import { existsSync as existsSync17 } from "node:fs";
33050
+ import { readFile as readFile8 } from "node:fs/promises";
33051
+ import { readdir as readdir8 } from "node:fs/promises";
32728
33052
  import { createHash as createHash12 } from "node:crypto";
32729
- import { join as join19, resolve as resolve25 } from "node:path";
33053
+ import { join as join20, resolve as resolve27 } from "node:path";
32730
33054
  import { compileProject as compileProject2 } from "@supacloud/compiler";
32731
33055
  var SAFE_TOKEN = /^[A-Za-z0-9._:@/+,-]+$/;
32732
33056
  var SAFE_REMOTE_ROOT = /^\/(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+\/?$/;
@@ -32743,7 +33067,6 @@ var remoteDevToolSchema = {
32743
33067
  delete: optional(Boolean2(), "Delete remote files absent locally"),
32744
33068
  reload: optional(Boolean2(), "Reload the affected target after sync (default: true)"),
32745
33069
  interval_ms: optional(Number2(), "Watch debounce interval in milliseconds (default: 300)"),
32746
- json: optional(Boolean2(), "Emit machine-readable JSON"),
32747
33070
  apply: optional(Boolean2(), "Apply generated migrations to the selected test database"),
32748
33071
  drizzle_config: optional(String2(), "Drizzle config path"),
32749
33072
  migrations_dir: optional(String2(), "Migration directory"),
@@ -32761,8 +33084,8 @@ async function runProcess(executable, args, cwd) {
32761
33084
  function resolveDrizzleCommand(root, configured) {
32762
33085
  if (configured?.trim())
32763
33086
  return configured.trim();
32764
- const local = join19(root, "node_modules", ".bin", "drizzle-kit");
32765
- return existsSync15(local) ? local : "drizzle-kit";
33087
+ const local = join20(root, "node_modules", ".bin", "drizzle-kit");
33088
+ return existsSync17(local) ? local : "drizzle-kit";
32766
33089
  }
32767
33090
  function toolFailed(value) {
32768
33091
  if (!value || typeof value !== "object")
@@ -32772,14 +33095,14 @@ function toolFailed(value) {
32772
33095
  return result.isError === true || content.some((chunk) => chunk !== null && typeof chunk === "object" && ("text" in chunk) && typeof chunk.text === "string" && chunk.text.trimStart().startsWith("❌"));
32773
33096
  }
32774
33097
  async function migrateDatabase(args, options) {
32775
- const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
33098
+ const root = resolve27(String(args.project_dir || options.cwd || process.cwd()));
32776
33099
  const projectConfig = await readProjectConfig(root);
32777
33100
  const config = projectConfig.dev || {};
32778
33101
  const database = config.database || {};
32779
33102
  const execute = options.execute || ((command, commandArgs, cwd) => runProcess(command, commandArgs, cwd));
32780
- const drizzleConfig = resolve25(root, String(args.drizzle_config || database.drizzleConfig || "drizzle.config.ts"));
33103
+ const drizzleConfig = resolve27(root, String(args.drizzle_config || database.drizzleConfig || "drizzle.config.ts"));
32781
33104
  const migrationsDir = String(args.migrations_dir || database.migrationsDir || "supabase/migrations");
32782
- if (!existsSync15(drizzleConfig))
33105
+ if (!existsSync17(drizzleConfig))
32783
33106
  throw new Error(`Drizzle config not found: ${drizzleConfig}`);
32784
33107
  const generated = await execute(resolveDrizzleCommand(root, typeof args.drizzle_bin === "string" ? args.drizzle_bin : database.drizzleBin), ["generate", "--config", drizzleConfig], root);
32785
33108
  if (generated.exitCode !== 0)
@@ -32797,11 +33120,11 @@ async function migrateDatabase(args, options) {
32797
33120
  return { ok: true, mode: "dev", action: "migrate", generated: true, applied: true, migrations_dir: migrationsDir, result: applied };
32798
33121
  }
32799
33122
  async function readProjectConfig(root) {
32800
- const file = join19(root, "supacloud.json");
32801
- if (!existsSync15(file))
33123
+ const file = join20(root, "supacloud.json");
33124
+ if (!existsSync17(file))
32802
33125
  return {};
32803
33126
  try {
32804
- const parsed = JSON.parse(await readFile7(file, "utf8"));
33127
+ const parsed = JSON.parse(await readFile8(file, "utf8"));
32805
33128
  return parsed && typeof parsed === "object" ? parsed : {};
32806
33129
  } catch (error) {
32807
33130
  throw new Error(`Invalid supacloud.json: ${error instanceof Error ? error.message : String(error)}`);
@@ -32814,9 +33137,9 @@ async function readDevConfig(root) {
32814
33137
  async function sourceFingerprint(root) {
32815
33138
  const files = [];
32816
33139
  const visit = async (directory) => {
32817
- const entries = await readdir7(directory, { withFileTypes: true });
33140
+ const entries = await readdir8(directory, { withFileTypes: true });
32818
33141
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
32819
- const path = join19(directory, entry.name);
33142
+ const path = join20(directory, entry.name);
32820
33143
  if (entry.isDirectory() && ![".git", "node_modules", "dist", "generated", ".supacloud"].includes(entry.name))
32821
33144
  await visit(path);
32822
33145
  else if (entry.isFile())
@@ -32827,7 +33150,7 @@ async function sourceFingerprint(root) {
32827
33150
  const hash = createHash12("sha256");
32828
33151
  for (const file of files) {
32829
33152
  hash.update(file.slice(root.length).replace(/\\/g, "/"));
32830
- hash.update(await readFile7(file));
33153
+ hash.update(await readFile8(file));
32831
33154
  }
32832
33155
  return hash.digest("hex");
32833
33156
  }
@@ -32844,19 +33167,19 @@ function remoteRoot(value) {
32844
33167
  }
32845
33168
  function targetDirectory(root, target, functionSlug, config = {}) {
32846
33169
  if (target === "db")
32847
- return join19(root, "supabase", "migrations");
33170
+ return join20(root, "supabase", "migrations");
32848
33171
  if (target === "functions") {
32849
33172
  const targets = config["targets"];
32850
33173
  const entries = targets && typeof targets === "object" ? Object.values(targets) : [];
32851
33174
  const match = entries.find((entry) => entry !== null && typeof entry === "object" && ("type" in entry) && entry.type === "edge_function" && (!("root" in entry) || typeof entry.root === "string") && (!("slug" in entry) || typeof entry.slug === "string") && (!functionSlug || ("slug" in entry) && entry.slug === functionSlug));
32852
- const base = match?.root ? resolve25(root, match.root) : join19(root, "supabase", "functions");
32853
- return match?.root ? base : functionSlug ? join19(base, safeToken(functionSlug, "function")) : base;
33175
+ const base = match?.root ? resolve27(root, match.root) : join20(root, "supabase", "functions");
33176
+ return match?.root ? base : functionSlug ? join20(base, safeToken(functionSlug, "function")) : base;
32854
33177
  }
32855
33178
  if (target === "frontend") {
32856
33179
  const targets = config["targets"];
32857
33180
  const entries = targets && typeof targets === "object" ? Object.values(targets) : [];
32858
33181
  const match = entries.find((entry) => entry !== null && typeof entry === "object" && ("type" in entry) && entry.type === "frontend" && (!("root" in entry) || typeof entry.root === "string"));
32859
- return match?.root ? resolve25(root, match.root) : join19(root, "apps", "web");
33182
+ return match?.root ? resolve27(root, match.root) : join20(root, "apps", "web");
32860
33183
  }
32861
33184
  return root;
32862
33185
  }
@@ -32895,10 +33218,10 @@ function reloadCommand(config, target, projectRef, functionSlug) {
32895
33218
  return [...command.split(" "), "--project-ref", safeToken(projectRef || "test", "project_ref"), "--target", targetConfigPath(target), ...functionSlug ? ["--function", safeToken(functionSlug, "function")] : []];
32896
33219
  }
32897
33220
  function sshArgs(connection, remoteCommand) {
32898
- return ["-p", String(connection.port), "-i", resolve25(connection.key), "-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=yes", `${connection.user}@${connection.host}`, ...remoteCommand];
33221
+ return ["-p", String(connection.port), "-i", resolve27(connection.key), "-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=yes", `${connection.user}@${connection.host}`, ...remoteCommand];
32899
33222
  }
32900
33223
  function rsyncArgs(connection, source, destination, excludes, deleteRemote) {
32901
- const args = ["-az", "--checksum", "--partial", "--protect-args", "-e", `ssh -p ${connection.port} -i ${resolve25(connection.key)} -o BatchMode=yes -o StrictHostKeyChecking=yes`];
33224
+ const args = ["-az", "--checksum", "--partial", "--protect-args", "-e", `ssh -p ${connection.port} -i ${resolve27(connection.key)} -o BatchMode=yes -o StrictHostKeyChecking=yes`];
32902
33225
  if (deleteRemote)
32903
33226
  args.push("--delete-delay");
32904
33227
  for (const exclude of excludes) {
@@ -32910,17 +33233,17 @@ function rsyncArgs(connection, source, destination, excludes, deleteRemote) {
32910
33233
  return args;
32911
33234
  }
32912
33235
  async function syncOnce(args, options) {
32913
- const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
33236
+ const root = resolve27(String(args.project_dir || options.cwd || process.cwd()));
32914
33237
  const projectConfig = await readProjectConfig(root);
32915
33238
  const config = projectConfig.dev || {};
32916
33239
  const target = String(args.target || "project");
32917
33240
  const source = targetDirectory(root, target, typeof args.function === "string" ? args.function : undefined, projectConfig);
32918
- if (!existsSync15(source))
33241
+ if (!existsSync17(source))
32919
33242
  throw new Error(`Dev source directory not found: ${source}`);
32920
33243
  let compiled = false;
32921
33244
  if (config.compile === true && target !== "db") {
32922
- const compileRoot = resolve25(root, config.compileRoot || ".");
32923
- const compileOutDir = resolve25(root, config.compileOutDir || "generated");
33245
+ const compileRoot = resolve27(root, config.compileRoot || ".");
33246
+ const compileOutDir = resolve27(root, config.compileOutDir || "generated");
32924
33247
  const compilation = await compileProject2({
32925
33248
  rootDir: compileRoot,
32926
33249
  outDir: compileOutDir,
@@ -32958,7 +33281,7 @@ function registerRemoteDevTools(server, options = {}) {
32958
33281
  if (["production", "prod"].includes((options.environment || "").toLowerCase()))
32959
33282
  throw new Error("Remote dev mode is forbidden for production environments");
32960
33283
  if (args.action === "status") {
32961
- const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
33284
+ const root = resolve27(String(args.project_dir || options.cwd || process.cwd()));
32962
33285
  const config = await readDevConfig(root);
32963
33286
  const connection = connectionArgs(options, config, args);
32964
33287
  const execute = options.execute || ((command, commandArgs, cwd) => runProcess(command, commandArgs, cwd));
@@ -32972,7 +33295,7 @@ function registerRemoteDevTools(server, options = {}) {
32972
33295
  return { content: [{ type: "text", text: JSON.stringify(await migrateDatabase(args, options), null, 2) }] };
32973
33296
  }
32974
33297
  if (args.action === "watch") {
32975
- const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
33298
+ const root = resolve27(String(args.project_dir || options.cwd || process.cwd()));
32976
33299
  const interval = Math.max(100, Math.min(1e4, Number(args.interval_ms || 300)));
32977
33300
  let fingerprint = "";
32978
33301
  let lastSync = null;
@@ -32992,215 +33315,19 @@ function registerRemoteDevTools(server, options = {}) {
32992
33315
  }
32993
33316
 
32994
33317
  // src/shared/tools/developer-experience-tools.ts
32995
- import { existsSync as existsSync17 } from "node:fs";
32996
- import { mkdir as mkdir7, readdir as readdir8, readFile as readFile8, writeFile as writeFile7 } from "node:fs/promises";
32997
- import { dirname as dirname14, join as join20, resolve as resolve27 } from "node:path";
32998
-
32999
- // src/shared/context.ts
33000
- import { existsSync as existsSync16, readFileSync as readFileSync12 } from "node:fs";
33001
- import { homedir as homedir3 } from "node:os";
33002
- import { resolve as resolve26 } from "node:path";
33003
- var CORE_CONTEXT_KEYS2 = [
33004
- "SUPABASE_URL",
33005
- "SUPABASE_SERVICE_ROLE_KEY",
33006
- "SUPACLOUD_API_URL",
33007
- "SUPACLOUD_MANAGEMENT_API_URL",
33008
- "MANAGEMENT_API_URL",
33009
- "SUPACLOUD_API_TOKEN",
33010
- "SUPACLOUD_PROJECT_REF",
33011
- "X_PROJECT_REF",
33012
- "SUPACLOUD_HOST",
33013
- "SUPACLOUD_APPLICATION_ID",
33014
- "SUPACLOUD_APPLICATION_ENVIRONMENT_ID",
33015
- "SUPACLOUD_APPLICATION_CONFIGURATION_PATH",
33016
- "SUPACLOUD_APPLICATION_CONFIGURATION_ID",
33017
- "SUPACLOUD_APPLICATION_MANIFEST_PATH"
33018
- ];
33019
- function unquotedEnvValue2(rawValue) {
33020
- const value = rawValue.trim();
33021
- const quote = value[0];
33022
- return quote && (quote === '"' || quote === "'") && value.endsWith(quote) ? value.slice(1, -1) : value;
33023
- }
33024
- function parseEnvFile2(contents) {
33025
- const values = {};
33026
- for (const rawLine of contents.split(/\r?\n/)) {
33027
- const line = rawLine.trim();
33028
- if (!line || line.startsWith("#"))
33029
- continue;
33030
- const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
33031
- if (!match)
33032
- continue;
33033
- values[match[1]] = unquotedEnvValue2(match[2]);
33034
- }
33035
- return values;
33036
- }
33037
- function readEnvFile2(path, required) {
33038
- if (!existsSync16(path)) {
33039
- if (required)
33040
- throw new Error(`SupaCloud environment file not found: ${path}`);
33041
- return {};
33042
- }
33043
- try {
33044
- return parseEnvFile2(readFileSync12(path, "utf8"));
33045
- } catch (error) {
33046
- const message = error instanceof Error ? error.message : String(error);
33047
- throw new Error(`Failed to read SupaCloud environment file ${path}: ${message}`);
33048
- }
33049
- }
33050
- function canonicalApiOrigin2(value) {
33051
- const candidate = value.trim();
33052
- if (!candidate)
33053
- return "";
33054
- try {
33055
- const url = new URL(candidate);
33056
- const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
33057
- const allowedProtocol = url.protocol === "https:" || url.protocol === "http:" && loopback;
33058
- const exactOrigin = candidate === url.origin || candidate === `${url.origin}/`;
33059
- return allowedProtocol && !url.username && !url.password && exactOrigin && url.pathname === "/" && !url.search && !url.hash ? url.origin : "";
33060
- } catch (error) {
33061
- if (error instanceof TypeError)
33062
- return "";
33063
- throw error;
33064
- }
33065
- }
33066
- function hostFromUrl2(value) {
33067
- return value ? new URL(value).hostname : "";
33068
- }
33069
- function inferProjectRefFromSupabaseUrl2(value) {
33070
- if (!value)
33071
- return "";
33072
- return new URL(value).hostname.match(/^([a-z0-9-]+)\.api\./i)?.[1] ?? "";
33073
- }
33074
- function sourceCredentialScope2(values, explicitApiUrl, supabaseUrl) {
33075
- const hasManagementContext = Boolean(explicitApiUrl.trim() || values.SUPACLOUD_API_TOKEN?.trim() || values.SUPACLOUD_HOST?.trim());
33076
- if (hasManagementContext)
33077
- return "management";
33078
- return supabaseUrl || values.SUPABASE_SERVICE_ROLE_KEY?.trim() ? "project_application" : "incomplete";
33079
- }
33080
- function sourceProjectCore2(values) {
33081
- const supabaseUrl = canonicalApiOrigin2(values.SUPABASE_URL || "");
33082
- const projectRef = (values.SUPACLOUD_PROJECT_REF || values.X_PROJECT_REF || "").trim() || inferProjectRefFromSupabaseUrl2(supabaseUrl);
33083
- const explicitApiUrl = values.SUPACLOUD_API_URL || values.SUPACLOUD_MANAGEMENT_API_URL || values.MANAGEMENT_API_URL || "";
33084
- const credentialScope = sourceCredentialScope2(values, explicitApiUrl, supabaseUrl);
33085
- const managementUrl = explicitApiUrl || (values.SUPACLOUD_HOST ? `http://${values.SUPACLOUD_HOST}:9090` : "");
33086
- const apiUrl = credentialScope === "management" ? canonicalApiOrigin2(managementUrl) : "";
33087
- const apiToken = credentialScope === "management" ? values.SUPACLOUD_API_TOKEN || "" : "";
33088
- return { apiUrl, apiToken, projectRef, supabaseUrl, credentialScope };
33089
- }
33090
- function processValues2(env) {
33091
- return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== undefined));
33092
- }
33093
- function hasProcessContext2(env) {
33094
- return CORE_CONTEXT_KEYS2.some((key) => Object.hasOwn(env, key));
33095
- }
33096
- function completeProjectContext2(values) {
33097
- const core = sourceProjectCore2(values);
33098
- if (core.credentialScope === "project_application") {
33099
- return Boolean(core.supabaseUrl && values.SUPABASE_SERVICE_ROLE_KEY?.trim() && core.projectRef);
33100
- }
33101
- return Boolean(core.apiUrl && core.apiToken && core.projectRef);
33102
- }
33103
- function parseInsecureTls2(values) {
33104
- for (const key of ["SUPACLOUD_TLS_VERIFY", "SUPACLOUD_TLS_INSECURE"]) {
33105
- const value = values[key]?.trim().toLowerCase();
33106
- if (value === undefined)
33107
- continue;
33108
- if (["1", "true", "yes", "on"].includes(value))
33109
- return key === "SUPACLOUD_TLS_INSECURE";
33110
- if (["0", "false", "no", "off"].includes(value))
33111
- return key === "SUPACLOUD_TLS_VERIFY";
33112
- throw new Error(`${key} must be a boolean (true or false)`);
33113
- }
33114
- return true;
33115
- }
33116
- function namedEnvironmentSource2(cwd, selector) {
33117
- const environment = normalizeEnvironmentName(selector);
33118
- const path = resolve26(cwd, `.env.supacloud.${selector}`);
33119
- const values = readEnvFile2(path, true);
33120
- if (values.SUPACLOUD_ENV && normalizeEnvironmentName(values.SUPACLOUD_ENV) !== environment) {
33121
- throw new Error(`SUPACLOUD_ENV in ${path} does not match selector ${selector}`);
33122
- }
33123
- return { values, kind: "named_env_file", path, environment };
33124
- }
33125
- function explicitEnvironmentSource2(cwd, envFile) {
33126
- const path = resolve26(cwd, envFile);
33127
- const values = readEnvFile2(path, true);
33128
- if (!values.SUPACLOUD_ENV)
33129
- throw new Error(`SUPACLOUD_ENV is required in ${path}`);
33130
- return {
33131
- values,
33132
- kind: "explicit_env_file",
33133
- path,
33134
- environment: normalizeEnvironmentName(values.SUPACLOUD_ENV)
33135
- };
33136
- }
33137
- function contextSource2(env, cwd, selection) {
33138
- if (selection.environmentName && selection.envFile)
33139
- throw new Error("Environment selectors are mutually exclusive");
33140
- if (selection.environmentName)
33141
- return namedEnvironmentSource2(cwd, selection.environmentName);
33142
- if (selection.envFile)
33143
- return explicitEnvironmentSource2(cwd, selection.envFile);
33144
- if (env.SUPACLOUD_ENV) {
33145
- const environment = normalizeEnvironmentName(env.SUPACLOUD_ENV);
33146
- const values = processValues2(env);
33147
- if (completeProjectContext2(values)) {
33148
- return { values, kind: "process_env", path: null, environment };
33149
- }
33150
- return namedEnvironmentSource2(cwd, env.SUPACLOUD_ENV);
33151
- }
33152
- if (hasProcessContext2(env)) {
33153
- return { values: processValues2(env), kind: "process_env", path: null, environment: "" };
33154
- }
33155
- const path = resolve26(cwd, ".env");
33156
- const values = readEnvFile2(path, false);
33157
- const environment = values.SUPACLOUD_ENV ? normalizeEnvironmentName(values.SUPACLOUD_ENV) : "";
33158
- return { values, kind: Object.keys(values).length ? "legacy_dotenv" : "none", path: existsSync16(path) ? path : null, environment };
33159
- }
33160
- function resolveSupaCloudContext2(env = process.env, cwd = process.cwd(), selection = {}) {
33161
- const source = contextSource2(env, cwd, selection);
33162
- const core = sourceProjectCore2(source.values);
33163
- const host = source.values.SUPACLOUD_HOST || hostFromUrl2(core.apiUrl || core.supabaseUrl);
33164
- const readOnly = env.SUPACLOUD_READ_ONLY === "true" || source.values.SUPACLOUD_READ_ONLY === "true";
33165
- const insecureTls = parseInsecureTls2(source.values);
33166
- return {
33167
- host,
33168
- sshUser: env.SUPACLOUD_SSH_USER ?? "root",
33169
- sshPort: parseInt(env.SUPACLOUD_SSH_PORT ?? "22", 10),
33170
- sshKey: env.SUPACLOUD_SSH_KEY ?? resolve26(homedir3(), ".ssh", "id_rsa"),
33171
- sshPass: env.SUPACLOUD_SSH_PASS ?? "",
33172
- apiUrl: core.apiUrl,
33173
- apiToken: core.apiToken,
33174
- projectRef: core.projectRef,
33175
- readOnly,
33176
- environment: source.environment,
33177
- production: source.environment === "prod" || source.environment === "production",
33178
- inferredSupabaseUrl: core.supabaseUrl,
33179
- inferredServiceRoleKey: source.values.SUPABASE_SERVICE_ROLE_KEY || "",
33180
- credentialScope: core.credentialScope,
33181
- source: source.kind,
33182
- sourcePath: source.path,
33183
- environmentVariables: source.values,
33184
- applicationId: source.values.SUPACLOUD_APPLICATION_ID?.trim() || "",
33185
- applicationEnvironmentId: source.values.SUPACLOUD_APPLICATION_ENVIRONMENT_ID?.trim() || source.environment,
33186
- applicationConfigurationPath: source.values.SUPACLOUD_APPLICATION_CONFIGURATION_PATH?.trim() || "",
33187
- applicationConfigurationId: source.values.SUPACLOUD_APPLICATION_CONFIGURATION_ID?.trim() || "",
33188
- applicationManifestPath: source.values.SUPACLOUD_APPLICATION_MANIFEST_PATH?.trim() || "",
33189
- insecureTls
33190
- };
33191
- }
33192
-
33193
- // src/shared/tools/developer-experience-tools.ts
33194
- var initToolSchema = {
33318
+ import { existsSync as existsSync18 } from "node:fs";
33319
+ import { mkdir as mkdir8, readdir as readdir9, readFile as readFile9, writeFile as writeFile8 } from "node:fs/promises";
33320
+ import { dirname as dirname15, join as join21, resolve as resolve28 } from "node:path";
33321
+ var initToolSchema2 = {
33195
33322
  root: optional(String2(), "Project directory to initialize (default: current directory)"),
33196
- json: optional(Boolean2(), "Emit one machine-readable JSON result")
33323
+ format: optional(stringEnum2(["text", "json"]), "Output format (default: text)")
33197
33324
  };
33198
- var environmentToolSchema = {
33325
+ var environmentToolSchema2 = {
33199
33326
  action: withDescription(stringEnum2(["list", "check"]), "Environment profile inspection action"),
33200
33327
  root: optional(String2(), "Project directory containing .env.supacloud.* files"),
33201
- json: optional(Boolean2(), "Emit one machine-readable JSON result")
33328
+ format: optional(stringEnum2(["text", "json"]), "Output format (default: text)")
33202
33329
  };
33203
- var TEST_PROFILE = [
33330
+ var TEST_PROFILE2 = [
33204
33331
  "SUPACLOUD_ENV=test",
33205
33332
  "SUPACLOUD_API_URL=https://management.test.example.com",
33206
33333
  "SUPACLOUD_API_TOKEN=<test-management-api-token>",
@@ -33209,7 +33336,7 @@ var TEST_PROFILE = [
33209
33336
  ""
33210
33337
  ].join(`
33211
33338
  `);
33212
- var PRODUCTION_PROFILE = [
33339
+ var PRODUCTION_PROFILE2 = [
33213
33340
  "SUPACLOUD_ENV=production",
33214
33341
  "SUPACLOUD_API_URL=https://management.example.com",
33215
33342
  "SUPACLOUD_API_TOKEN=<production-management-api-token>",
@@ -33218,41 +33345,41 @@ var PRODUCTION_PROFILE = [
33218
33345
  ""
33219
33346
  ].join(`
33220
33347
  `);
33221
- function textResult3(text, isError = false) {
33348
+ function textResult4(text, isError = false) {
33222
33349
  return { isError, content: [{ type: "text", text }] };
33223
33350
  }
33224
- function jsonResult(value, isError = false) {
33225
- return textResult3(JSON.stringify(value, null, 2), isError);
33351
+ function jsonResult2(value, isError = false) {
33352
+ return textResult4(JSON.stringify(value, null, 2), isError);
33226
33353
  }
33227
- function record6(value) {
33354
+ function record7(value) {
33228
33355
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
33229
33356
  }
33230
- function packageDependencies(manifest) {
33357
+ function packageDependencies2(manifest) {
33231
33358
  return new Set([
33232
33359
  ...Object.keys(manifest.dependencies ?? {}),
33233
33360
  ...Object.keys(manifest.devDependencies ?? {})
33234
33361
  ]);
33235
33362
  }
33236
- function outputDirectoryFor(manifest) {
33237
- const dependencies = packageDependencies(manifest);
33363
+ function outputDirectoryFor2(manifest) {
33364
+ const dependencies = packageDependencies2(manifest);
33238
33365
  if (dependencies.has("next"))
33239
33366
  return "out";
33240
33367
  if (dependencies.has("@sveltejs/kit") || dependencies.has("svelte"))
33241
33368
  return "build";
33242
33369
  return "dist";
33243
33370
  }
33244
- async function readPackageManifest(root) {
33245
- const path = join20(root, "package.json");
33246
- if (!existsSync17(path))
33371
+ async function readPackageManifest2(root) {
33372
+ const path = join21(root, "package.json");
33373
+ if (!existsSync18(path))
33247
33374
  return {};
33248
33375
  try {
33249
- const parsed = JSON.parse(await readFile8(path, "utf8"));
33250
- const value = record6(parsed);
33376
+ const parsed = JSON.parse(await readFile9(path, "utf8"));
33377
+ const value = record7(parsed);
33251
33378
  if (!value)
33252
33379
  throw new Error("package.json must contain an object");
33253
- const scripts = record6(value.scripts);
33254
- const dependencies = record6(value.dependencies);
33255
- const devDependencies = record6(value.devDependencies);
33380
+ const scripts = record7(value.scripts);
33381
+ const dependencies = record7(value.dependencies);
33382
+ const devDependencies = record7(value.devDependencies);
33256
33383
  return {
33257
33384
  scripts: scripts ?? undefined,
33258
33385
  dependencies: dependencies ?? undefined,
@@ -33262,9 +33389,9 @@ async function readPackageManifest(root) {
33262
33389
  throw new Error(`Invalid package.json: ${error instanceof Error ? error.message : String(error)}`);
33263
33390
  }
33264
33391
  }
33265
- async function writeNewFile(path, content) {
33392
+ async function writeNewFile2(path, content) {
33266
33393
  try {
33267
- await writeFile7(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
33394
+ await writeFile8(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
33268
33395
  return true;
33269
33396
  } catch (error) {
33270
33397
  if (error instanceof Error && "code" in error && error.code === "EEXIST")
@@ -33272,9 +33399,9 @@ async function writeNewFile(path, content) {
33272
33399
  throw error;
33273
33400
  }
33274
33401
  }
33275
- async function ensureGitignore(root) {
33276
- const path = join20(root, ".gitignore");
33277
- const existing = existsSync17(path) ? await readFile8(path, "utf8") : "";
33402
+ async function ensureGitignore2(root) {
33403
+ const path = join21(root, ".gitignore");
33404
+ const existing = existsSync18(path) ? await readFile9(path, "utf8") : "";
33278
33405
  const required = [".env.supacloud.*", "!.env.supacloud.*.example"];
33279
33406
  const missing = required.filter((entry) => !existing.split(/\r?\n/).includes(entry));
33280
33407
  if (missing.length === 0)
@@ -33282,21 +33409,21 @@ async function ensureGitignore(root) {
33282
33409
  const prefix = existing.length > 0 && !existing.endsWith(`
33283
33410
  `) ? `${existing}
33284
33411
  ` : existing;
33285
- await writeFile7(path, `${prefix}${missing.join(`
33412
+ await writeFile8(path, `${prefix}${missing.join(`
33286
33413
  `)}
33287
33414
  `, "utf8");
33288
33415
  return true;
33289
33416
  }
33290
- async function initializeProject(request, options) {
33291
- const root = resolve27(request.root || options.cwd || process.cwd());
33292
- await mkdir7(root, { recursive: true });
33293
- const configPath = join20(root, "supacloud.json");
33294
- if (existsSync17(configPath)) {
33417
+ async function initializeProject2(request, options = {}) {
33418
+ const root = resolve28(request.root || options.cwd || process.cwd());
33419
+ await mkdir8(root, { recursive: true });
33420
+ const configPath = join21(root, "supacloud.json");
33421
+ if (existsSync18(configPath)) {
33295
33422
  throw new Error(`supacloud.json already exists in ${root}; init never overwrites project configuration`);
33296
33423
  }
33297
- const manifest = await readPackageManifest(root);
33424
+ const manifest = await readPackageManifest2(root);
33298
33425
  const hasBuildScript = typeof manifest.scripts?.build === "string" && Boolean(manifest.scripts.build.trim());
33299
- const outputDirectory = outputDirectoryFor(manifest);
33426
+ const outputDirectory = outputDirectoryFor2(manifest);
33300
33427
  const config = {
33301
33428
  frontend: {
33302
33429
  root: ".",
@@ -33305,7 +33432,7 @@ async function initializeProject(request, options) {
33305
33432
  };
33306
33433
  const created = [];
33307
33434
  try {
33308
- await writeFile7(configPath, `${JSON.stringify(config, null, 2)}
33435
+ await writeFile8(configPath, `${JSON.stringify(config, null, 2)}
33309
33436
  `, {
33310
33437
  encoding: "utf8",
33311
33438
  flag: "wx",
@@ -33318,13 +33445,13 @@ async function initializeProject(request, options) {
33318
33445
  throw error;
33319
33446
  }
33320
33447
  created.push("supacloud.json");
33321
- if (await writeNewFile(join20(root, ".env.supacloud.test.example"), TEST_PROFILE)) {
33448
+ if (await writeNewFile2(join21(root, ".env.supacloud.test.example"), TEST_PROFILE2)) {
33322
33449
  created.push(".env.supacloud.test.example");
33323
33450
  }
33324
- if (await writeNewFile(join20(root, ".env.supacloud.production.example"), PRODUCTION_PROFILE)) {
33451
+ if (await writeNewFile2(join21(root, ".env.supacloud.production.example"), PRODUCTION_PROFILE2)) {
33325
33452
  created.push(".env.supacloud.production.example");
33326
33453
  }
33327
- if (await ensureGitignore(root))
33454
+ if (await ensureGitignore2(root))
33328
33455
  created.push(".gitignore");
33329
33456
  const result = {
33330
33457
  schema: "supacloud.developer-experience.init.v1",
@@ -33332,7 +33459,7 @@ async function initializeProject(request, options) {
33332
33459
  root,
33333
33460
  created,
33334
33461
  detected: {
33335
- package_json: existsSync17(join20(root, "package.json")),
33462
+ package_json: existsSync18(join21(root, "package.json")),
33336
33463
  build_script: hasBuildScript,
33337
33464
  output_directory: outputDirectory
33338
33465
  },
@@ -33343,9 +33470,9 @@ async function initializeProject(request, options) {
33343
33470
  "supacloud --env test deploy"
33344
33471
  ]
33345
33472
  };
33346
- if (request.json === true)
33347
- return jsonResult(result);
33348
- return textResult3([
33473
+ if (request.format === "json")
33474
+ return jsonResult2(result);
33475
+ return textResult4([
33349
33476
  `Initialized SupaCloud deployment in ${root}.`,
33350
33477
  `Created: ${created.join(", ")}.`,
33351
33478
  hasBuildScript ? `Detected package.json build script; deploy output defaults to ${outputDirectory}.` : "No package.json build script detected; add one before deploying.",
@@ -33359,14 +33486,14 @@ function profileNameFromFile(name) {
33359
33486
  return match?.[1] ?? null;
33360
33487
  }
33361
33488
  async function environmentProfiles(root) {
33362
- const entries = await readdir8(root, { withFileTypes: true }).catch((error) => {
33489
+ const entries = await readdir9(root, { withFileTypes: true }).catch((error) => {
33363
33490
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
33364
33491
  return [];
33365
33492
  throw error;
33366
33493
  });
33367
33494
  return entries.filter((entry) => entry.isFile()).map((entry) => {
33368
33495
  const name = profileNameFromFile(entry.name);
33369
- return name ? { name, path: join20(root, entry.name), exists: true } : null;
33496
+ return name ? { name, path: join21(root, entry.name), exists: true } : null;
33370
33497
  }).filter((profile) => profile !== null).sort((left, right) => left.name.localeCompare(right.name));
33371
33498
  }
33372
33499
  function statusMissing(context) {
@@ -33401,18 +33528,18 @@ function deployMissing(context, application, hasFrontend = false) {
33401
33528
  }
33402
33529
  async function inspectProfile(profile) {
33403
33530
  try {
33404
- const context = resolveSupaCloudContext2({}, dirname14(profile.path), {
33531
+ const context = resolveSupaCloudContext2({}, dirname15(profile.path), {
33405
33532
  environmentName: profile.name
33406
33533
  });
33407
33534
  const status = statusMissing(context);
33408
- const configText = await readFile8(join20(dirname14(profile.path), "supacloud.json"), "utf8").catch((error) => {
33535
+ const configText = await readFile9(join21(dirname15(profile.path), "supacloud.json"), "utf8").catch((error) => {
33409
33536
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
33410
33537
  return "{}";
33411
33538
  throw error;
33412
33539
  });
33413
- const config = record6(JSON.parse(configText));
33414
- const hasFrontend = record6(config?.frontend) !== null || Object.values(record6(config?.targets) ?? {}).some((target) => record6(target)?.type === "frontend");
33415
- const deploy = deployMissing(context, record6(config?.application), hasFrontend);
33540
+ const config = record7(JSON.parse(configText));
33541
+ const hasFrontend = record7(config?.frontend) !== null || Object.values(record7(config?.targets) ?? {}).some((target) => record7(target)?.type === "frontend");
33542
+ const deploy = deployMissing(context, record7(config?.application), hasFrontend);
33416
33543
  return {
33417
33544
  name: profile.name,
33418
33545
  path: profile.path,
@@ -33449,7 +33576,7 @@ async function inspectProfile(profile) {
33449
33576
  }
33450
33577
  }
33451
33578
  async function inspectEnvironment(request, options) {
33452
- const root = resolve27(request.root || options.cwd || process.cwd());
33579
+ const root = resolve28(request.root || options.cwd || process.cwd());
33453
33580
  const profiles = await environmentProfiles(root);
33454
33581
  if (request.action === "list") {
33455
33582
  const active = options.activeContext?.environment || null;
@@ -33464,13 +33591,13 @@ async function inspectEnvironment(request, options) {
33464
33591
  active: profile.name.toLowerCase() === active?.toLowerCase()
33465
33592
  }))
33466
33593
  };
33467
- if (request.json === true)
33468
- return jsonResult(result);
33594
+ if (request.format === "json")
33595
+ return jsonResult2(result);
33469
33596
  if (profiles.length === 0) {
33470
- return textResult3(`No .env.supacloud.<name> profiles found in ${root}.
33471
- Run \`supacloud init\` to create safe examples.`);
33597
+ return textResult4(`No .env.supacloud.<name> profiles found in ${root}.
33598
+ Run \`supacloud deploy init\` to create safe examples.`);
33472
33599
  }
33473
- return textResult3([
33600
+ return textResult4([
33474
33601
  `Environment profiles in ${root}:`,
33475
33602
  ...result.environments.map((profile) => `${profile.active ? "*" : " "} ${profile.name} ${profile.path}`)
33476
33603
  ].join(`
@@ -33484,11 +33611,11 @@ Run \`supacloud init\` to create safe examples.`);
33484
33611
  active: options.activeContext?.environment || null,
33485
33612
  environments: reports
33486
33613
  };
33487
- if (request.json === true)
33488
- return jsonResult(result, !result.ok);
33614
+ if (request.format === "json")
33615
+ return jsonResult2(result, !result.ok);
33489
33616
  if (reports.length === 0) {
33490
- return textResult3(`No environment profiles found in ${root}.
33491
- Run \`supacloud init\` and copy an example profile.`, true);
33617
+ return textResult4(`No environment profiles found in ${root}.
33618
+ Run \`supacloud deploy init\` and copy an example profile.`, true);
33492
33619
  }
33493
33620
  const lines = reports.map((report) => {
33494
33621
  if (report.error)
@@ -33498,12 +33625,11 @@ Run \`supacloud init\` and copy an example profile.`, true);
33498
33625
  const ref = report.projectRef ? `project=${report.projectRef}` : "project=unknown";
33499
33626
  return `${status} ${report.name} ${deploy} ${ref}`;
33500
33627
  });
33501
- return textResult3(lines.join(`
33628
+ return textResult4(lines.join(`
33502
33629
  `), !result.ok);
33503
33630
  }
33504
33631
  function registerDeveloperExperienceTools(server, options = {}) {
33505
- registerTool(server, "init", "Create SupaCloud deployment configuration and non-secret environment examples without overwriting project files.", initToolSchema, (request) => initializeProject(request, options));
33506
- registerTool(server, "env", "List or locally validate named SupaCloud environment profiles without contacting the platform or printing secrets.", environmentToolSchema, (request) => inspectEnvironment(request, options));
33632
+ registerTool(server, "env", "List or locally validate named SupaCloud environment profiles without contacting the platform or printing secrets.", environmentToolSchema2, (request) => inspectEnvironment(request, options));
33507
33633
  }
33508
33634
 
33509
33635
  // src/shared/tools/project-deployment.ts
@@ -33631,7 +33757,7 @@ async function runProjectDeployment(http, args) {
33631
33757
  // package.json
33632
33758
  var package_default = {
33633
33759
  name: "@supacloud/cli",
33634
- version: "0.113.0",
33760
+ version: "0.115.0",
33635
33761
  description: "Project-scoped CLI for SupaCloud users",
33636
33762
  type: "module",
33637
33763
  main: "./dist/index.js",
@@ -33683,9 +33809,9 @@ var package_default = {
33683
33809
  directory: "packages/cli"
33684
33810
  },
33685
33811
  dependencies: {
33686
- "@supacloud/compiler": "^0.53.0",
33812
+ "@supacloud/compiler": "^0.54.0",
33687
33813
  "@supacloud/delivery": "0.24.0",
33688
- "@supacloud/db": "0.20.3",
33814
+ "@supacloud/db": "0.20.4",
33689
33815
  typebox: "1.4.3"
33690
33816
  },
33691
33817
  overrides: {
@@ -33695,10 +33821,10 @@ var package_default = {
33695
33821
  },
33696
33822
  devDependencies: {
33697
33823
  "@supabase/supabase-js": "2.118.0",
33698
- "@supacloud/elysia": "0.37.0",
33824
+ "@supacloud/elysia": "0.38.0",
33699
33825
  "@types/bun": "^1.4.4",
33700
33826
  effect: "4.0.5",
33701
- elysia: "2.0.0-rc.0",
33827
+ elysia: "2.0.0-rc.1",
33702
33828
  typescript: "^7.0.2"
33703
33829
  }
33704
33830
  };
@@ -33867,7 +33993,7 @@ USAGE
33867
33993
 
33868
33994
  ${preferredCommand} [global flags] deploy [--flags]
33869
33995
  ${preferredCommand} [global flags] <module> <action> [--flags]
33870
- ${preferredCommand} init [--root <directory>]
33996
+ ${preferredCommand} deploy init [--root <directory>]
33871
33997
  ${preferredCommand} env <list|check> [--root <directory>]
33872
33998
  ${preferredCommand} [global flags] status
33873
33999
  ${preferredCommand} --help
@@ -33901,9 +34027,9 @@ DEFAULT CONTEXT
33901
34027
 
33902
34028
  EXAMPLES
33903
34029
 
33904
- ${preferredCommand} init
34030
+ ${preferredCommand} deploy init
33905
34031
  ${preferredCommand} env check
33906
- ${preferredCommand} env check --json
34032
+ ${preferredCommand} env check --format json
33907
34033
  ${preferredCommand} status
33908
34034
  ${preferredCommand} deploy
33909
34035
  ${preferredCommand} --env test deploy
@@ -33938,7 +34064,7 @@ EXAMPLES
33938
34064
  ${preferredCommand} supabase db_dump --db_url "postgresql://..." --file backups/schema.sql
33939
34065
  ${preferredCommand} lite migrate --project_dir .
33940
34066
  ${preferredCommand} lite start --project_dir . --port 54321
33941
- ${preferredCommand} lite doctor --project_dir . --json
34067
+ ${preferredCommand} lite doctor --project_dir . --format json
33942
34068
  ${preferredCommand} branch create --name feature-auth --data_mode schema_only
33943
34069
  ${preferredCommand} branch promotion_plan --branch_ref preview123
33944
34070
  ${preferredCommand} branch promote --branch_ref preview123 --plan_checksum <sha256>
@@ -33946,8 +34072,6 @@ EXAMPLES
33946
34072
  ${preferredCommand} ai install_skill --dry_run
33947
34073
  ${preferredCommand} mcp config
33948
34074
  ${preferredCommand} app init --name my-service
33949
- ${preferredCommand} app init --name my-api --template http
33950
- ${preferredCommand} app init --name my-worker --template edge
33951
34075
  ${preferredCommand} app generate --kind module --name billing
33952
34076
  ${preferredCommand} app generate --kind command --module billing --name issue-invoice
33953
34077
  ${preferredCommand} app generate --kind job --module billing --name sync-orders
@@ -33988,13 +34112,6 @@ EXAMPLES
33988
34112
  ${preferredCommand} app context --root . --target CaseModule --events execution.ndjson --request-id request-123 --format json
33989
34113
  ${preferredCommand} app doctor --root .
33990
34114
  ${preferredCommand} app fix --root . --fix fix.json --write
33991
- ${preferredCommand} generate --kind module --name billing
33992
- ${preferredCommand} generate --kind job --module billing --name sync-orders
33993
- ${preferredCommand} generate --kind contract --module billing --name issue-invoice
33994
- ${preferredCommand} check --root . --strict
33995
- ${preferredCommand} context --format json
33996
- ${preferredCommand} context --target CaseModule --format json
33997
- ${preferredCommand} doctor
33998
34115
  ${preferredCommand} db context --root . --module_file db/modules.ts --module cases
33999
34116
  ${preferredCommand} db context --root . --module_file db/modules.ts --target public.case_create
34000
34117
  ${preferredCommand} db lint --root . --module_file db/modules.ts
@@ -34006,6 +34123,7 @@ EXAMPLES
34006
34123
  ${preferredCommand} db plan --dir supabase/migrations
34007
34124
  ${preferredCommand} db apply --dir supabase/migrations --approved_digest <reviewed-digest>
34008
34125
  ${preferredCommand} db role_check --schema public
34126
+ ${preferredCommand} db role_check --remote --schema public
34009
34127
  ${preferredCommand} db role_sql --application_role app_user --migration_role app_migrator --database app_db
34010
34128
  ${preferredCommand} edge_functions get_config --ref abc123 --slug hello
34011
34129
  ${preferredCommand} edge_functions deploy --ref abc123 --slug hello --path ./supabase/functions/hello --expected-active-version absent --expected-activation-id legacy
@@ -34068,7 +34186,6 @@ function createCliTools(context, confirmProduction) {
34068
34186
  activeContext: context
34069
34187
  })));
34070
34188
  Object.assign(tools, captureTools((server) => registerAppTools(server, { onDevProgress: writeDevProgress })));
34071
- Object.assign(tools, captureTools((server) => registerAppAliases(server)));
34072
34189
  Object.assign(tools, captureTools((server) => registerDbGovernanceTools(server, {
34073
34190
  projectRef: context.projectRef || undefined,
34074
34191
  apiUrl: context.apiUrl || undefined,
@@ -34103,7 +34220,7 @@ function createCliTools(context, confirmProduction) {
34103
34220
  ]
34104
34221
  })
34105
34222
  };
34106
- for (const name of ["database", "auth", "oauth_clients", "storage", "edge_functions", "secrets", "frontend", "applications", "queue", "task_events", "scheduled_functions", "mutations", "diagnostics", "gateway", "branch", "release"]) {
34223
+ for (const name of ["database", "auth", "oauth_clients", "storage", "edge_functions", "secrets", "frontend", "queue", "task_events", "scheduled_functions", "mutations", "diagnostics", "gateway", "branch", "release"]) {
34107
34224
  tools[name] = {
34108
34225
  schema: { action: genericActionSchema },
34109
34226
  callback: async () => ({
@@ -34130,7 +34247,6 @@ function createCliTools(context, confirmProduction) {
34130
34247
  tools.branch = { schema: branchHelpTool.schema, callback: branchContextCallback };
34131
34248
  }
34132
34249
  const frontendContextCallback = tools.frontend.callback;
34133
- tools.applications = { schema: APPLICATION_TOOL_SCHEMA, callback: tools.applications.callback };
34134
34250
  const frontendHelpTool = captureTools((server) => registerFrontendTools(server, {})).frontend;
34135
34251
  if (frontendHelpTool) {
34136
34252
  tools.frontend = { schema: frontendHelpTool.schema, callback: frontendContextCallback };
@@ -34140,13 +34256,22 @@ function createCliTools(context, confirmProduction) {
34140
34256
  registerContextAwareHelp();
34141
34257
  tools.deploy = {
34142
34258
  schema: deployToolSchema,
34143
- callback: async () => ({
34144
- isError: true,
34145
- content: [{
34146
- type: "text",
34147
- text: `\u26A0\uFE0F Deploy requires Management API context. Run \`${preferredCommand} status\` to inspect current detection.`
34148
- }]
34149
- })
34259
+ callback: async (args) => {
34260
+ const input = args && typeof args === "object" ? args : {};
34261
+ if (input.action === "init") {
34262
+ return initializeProject2({
34263
+ ...typeof input.root === "string" ? { root: input.root } : {},
34264
+ ...input.format === "json" ? { format: "json" } : {}
34265
+ }, { cwd: process.cwd() });
34266
+ }
34267
+ return {
34268
+ isError: true,
34269
+ content: [{
34270
+ type: "text",
34271
+ text: `\u26A0\uFE0F Deploy requires Management API context. Run \`${preferredCommand} status\` to inspect current detection.`
34272
+ }]
34273
+ };
34274
+ }
34150
34275
  };
34151
34276
  Object.assign(tools, captureTools((server) => registerDatabaseTools(server, undefined, {
34152
34277
  localOnly: true
@@ -34245,9 +34370,9 @@ function createCliTools(context, confirmProduction) {
34245
34370
  applicationOrigin: context.inferredSupabaseUrl || undefined
34246
34371
  })));
34247
34372
  assign(captureTools((server) => registerFrontendTools(server, http)));
34248
- assign(captureTools((server) => registerApplicationTools(server, http)));
34373
+ const applicationTools = captureTools((server) => registerApplicationTools(server, http));
34249
34374
  Object.assign(tools, captureTools((server) => registerAppTools(server, {
34250
- getApplications: () => tools.applications?.callback,
34375
+ getApplications: () => applicationTools.applications?.callback,
34251
34376
  onDevProgress: writeDevProgress,
34252
34377
  projectRef: context.projectRef || undefined,
34253
34378
  projectDeployment: (args) => runProjectDeployment(http, args)
@@ -34302,7 +34427,7 @@ async function main() {
34302
34427
  }
34303
34428
  const cliTools = createCliTools(context, globalOptions.confirmProduction);
34304
34429
  if (args.length === 1 && !["ai", "supabase", "lite", "app", "db", "env"].includes(args[0]) && cliTools[args[0]]) {
34305
- if ("action" in cliTools[args[0]].schema) {
34430
+ if ("action" in cliTools[args[0]].schema && args[0] !== "deploy") {
34306
34431
  if (context.credentialScope !== "management" || !context.apiUrl || !context.apiToken) {
34307
34432
  console.error(`This command requires Management API context. Run \`${preferredCommand} status\` to inspect current detection.`);
34308
34433
  process.exitCode = 1;