@supacloud/cli 0.114.0 → 0.115.1

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", "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"],
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
  },
@@ -12930,7 +12927,9 @@ function registerDatabaseTools(server, http, config = {}) {
12930
12927
  "db_lint",
12931
12928
  "rpc_catalog",
12932
12929
  "list_rpcs",
12933
- "migration_import"
12930
+ "migration_import",
12931
+ "governance_check",
12932
+ "role_check"
12934
12933
  ];
12935
12934
  const writeActions = ["execute", "apply_migration", "push_migrations", "baseline_migrations", "create_table_rls", "enable_extension", "disable_extension"];
12936
12935
  const remoteActions = [...readActions, ...localActions];
@@ -12948,10 +12947,11 @@ Actions: ${allActions.join(", ")}${localOnly ? " (local-only mode)" : readOnly ?
12948
12947
  strict: optional(Boolean2(), "[push_migrations/lint_migrations/lint/database_lint] Exit with error if high-risk issues or migrations are detected"),
12949
12948
  fail_on_high: optional(Boolean2(), "[push_migrations/lint_migrations/lint/database_lint] Alias for --strict"),
12950
12949
  fail_on_medium: optional(Boolean2(), "[push_migrations/lint_migrations/lint] Exit with error if medium-risk or high-risk migrations are detected"),
12951
- 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)"),
12952
12951
  schema: optional(String2(), "[describe_columns/list_indexes/list_constraints/rls_status/rls_policies/create_table_rls/database_lint] Schema name (default: public)"),
12953
12952
  table: optional(String2(), "[describe_columns/indexes/constraints/rls_*] Table name"),
12954
- 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"),
12955
12955
  user_id: optional(String2(), "[get_auth_user] User UUID"),
12956
12956
  limit: optional(Number2(), "[list_auth_users] Max users (default: 20)"),
12957
12957
  name: optional(String2(), "[apply_migration] Migration name"),
@@ -13109,17 +13109,21 @@ Actions: ${allActions.join(", ")}${localOnly ? " (local-only mode)" : readOnly ?
13109
13109
  case "export_svadmin_schema": {
13110
13110
  if (!args.table)
13111
13111
  throw new Error("'table' required");
13112
- const schemaLiteral = sqlStringLiteral(schema, "schema");
13113
- const tableLiteral = sqlStringLiteral(args.table, "table");
13114
- const qualifiedTable = `${quoteIdentifier(schema, "schema")}.${quoteIdentifier(args.table, "table")}`;
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}'`;
13115
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;`;
13116
13118
  const colRes = await execSql(colSql);
13117
13119
  if (!colRes.ok)
13118
13120
  throw new Error(`Failed to inspect columns (${colRes.status})`);
13119
- const conSql = `SELECT conname as name, contype as type, pg_get_constraintdef(oid) as definition FROM pg_constraint WHERE connamespace = ${schemaLiteral}::regnamespace AND conrelid = ${qualifiedTable}::regclass;`;
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;`;
13120
13122
  const conRes = await execSql(conSql);
13121
- const cols = buildSvadminColumns(colRes.data, conRes.ok ? conRes.data : []);
13122
- if (args.json) {
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") {
13123
13127
  return {
13124
13128
  content: [{
13125
13129
  type: "text",
@@ -13295,7 +13299,7 @@ Actions: ${allActions.join(", ")}${localOnly ? " (local-only mode)" : readOnly ?
13295
13299
  migrationFiles = sortMigrationFiles(files.map((file) => readMigrationFile(dir, file)));
13296
13300
  }
13297
13301
  const analysis = analyzeMigrationFiles(migrationFiles);
13298
- if (args.json) {
13302
+ if (args.format === "json") {
13299
13303
  text = JSON.stringify(analysis, null, 2);
13300
13304
  } else {
13301
13305
  text = formatMigrationRiskReport(analysis);
@@ -13530,7 +13534,7 @@ ${riskReport}`
13530
13534
  if (!r.ok) {
13531
13535
  return { content: [{ type: "text", text: `❌ Database lint request failed (${r.status})` }], isError: true };
13532
13536
  }
13533
- if (args.json) {
13537
+ if (args.format === "json") {
13534
13538
  text = JSON.stringify(r.data, null, 2);
13535
13539
  } else {
13536
13540
  text = formatDatabaseLintReport(r.data);
@@ -13545,6 +13549,45 @@ ${riskReport}`
13545
13549
  }
13546
13550
  break;
13547
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
+ }
13548
13591
  case "rpc_catalog":
13549
13592
  case "list_rpcs": {
13550
13593
  const schemasList = args.schemas && args.schemas.length > 0 ? args.schemas : args.schema ? [args.schema] : ["public", "api"];
@@ -13553,7 +13596,7 @@ ${riskReport}`
13553
13596
  if (!r.ok) {
13554
13597
  return { content: [{ type: "text", text: `❌ RPC catalog request failed (${r.status})` }], isError: true };
13555
13598
  }
13556
- if (args.json) {
13599
+ if (args.format === "json") {
13557
13600
  text = JSON.stringify(r.data, null, 2);
13558
13601
  } else {
13559
13602
  text = formatRpcCatalog(r.data);
@@ -13569,9 +13612,6 @@ ${riskReport}`
13569
13612
  function quoteIdentifier(value, label) {
13570
13613
  return `"${validatedIdentifier(value, label)}"`;
13571
13614
  }
13572
- function sqlStringLiteral(value, label) {
13573
- return `'${validatedIdentifier(value, label)}'`;
13574
- }
13575
13615
  function validatedIdentifier(value, label) {
13576
13616
  if (typeof value !== "string" || !/^[A-Za-z_][A-Za-z0-9_]{0,62}$/.test(value)) {
13577
13617
  throw new Error(`Invalid ${label} identifier`);
@@ -13632,6 +13672,34 @@ function formatDatabaseLintReport(data) {
13632
13672
  return lines.join(`
13633
13673
  `).trimEnd();
13634
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
+ }
13635
13703
  function formatRpcCatalog(data) {
13636
13704
  if (!data || typeof data !== "object")
13637
13705
  return JSON.stringify(data, null, 2);
@@ -13795,17 +13863,17 @@ function buildSvadminColumns(colData, conData) {
13795
13863
  });
13796
13864
  }
13797
13865
  function formatSvadminSchemaExport(table, columns) {
13798
- const jsonPayload = JSON.stringify(columns, null, 2);
13799
- return `\uD83D\uDE80 SVAdmin Schema Definition for '${table}'
13866
+ const payload = {
13867
+ resource: table,
13868
+ sourceType: "databaseColumns",
13869
+ columns
13870
+ };
13871
+ return `SVAdmin schema for '${table}'
13800
13872
 
13801
- ` + `Pass this payload directly to svadmin_generateResource to produce complete UI components:
13802
- ` + ` svadmin_generateResource({
13803
- ` + ` resource: '${table}',
13804
- ` + ` sourceType: 'databaseColumns',
13805
- ` + ` payload: ${JSON.stringify(jsonPayload)}
13806
- ` + ` })
13873
+ ` + `Pass this object to svadmin_generateResource. The CLI does not generate the resource.
13874
+ ` + `${JSON.stringify(payload, null, 2)}
13807
13875
 
13808
- ` + `Column Metadata (${columns.length} columns):
13876
+ ` + `Column metadata (${columns.length}):
13809
13877
  ` + columns.map((c) => ` - ${c.name}: ${c.type}${c.isPrimary ? " [PK]" : ""}${c.isForeignKey ? ` [FK -> ${c.referencesResource ?? "inferred"}]` : ""}${c.nullable ? " (nullable)" : " (required)"}`).join(`
13810
13878
  `);
13811
13879
  }
@@ -15629,7 +15697,7 @@ function scaffoldFiles(framework, slug) {
15629
15697
  "package.json": JSON.stringify({
15630
15698
  private: true,
15631
15699
  type: "module",
15632
- dependencies: { elysia: "2.0.0-rc.0" }
15700
+ dependencies: { elysia: "2.0.0-rc.1" }
15633
15701
  }, null, 2) + `
15634
15702
  `,
15635
15703
  "index.ts": `import { Elysia } from "elysia";
@@ -20801,13 +20869,13 @@ function registerBranchTools(server, http, options = {}) {
20801
20869
  data_mode: optional(stringEnum2(["schema_only", "full_clone"]), "[create] Preview data mode (default: schema_only)"),
20802
20870
  plan_checksum: optional(String2(), "[promote] Reviewed plan checksum from promotion_plan"),
20803
20871
  confirm_destructive: optional(Boolean2(), "[promote] Confirm reviewed destructive migrations"),
20804
- json: optional(Boolean2(), "[promotion_plan/promote] Return machine-readable JSON")
20872
+ format: optional(stringEnum2(["text", "json"]), "[promotion_plan/promote] Output format (default: text)")
20805
20873
  }, async (args) => {
20806
20874
  const action = requireString(args.action, "action");
20807
20875
  const ref = resolveProjectRef(args.ref, options.projectRef);
20808
20876
  const writeAction = action === "create" || action === "delete" || action === "promote";
20809
20877
  if (writeAction && options.readOnly) {
20810
- 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, {
20811
20879
  project_ref: ref,
20812
20880
  branch_ref: args.branch_ref,
20813
20881
  reason: "READ_ONLY"
@@ -20833,13 +20901,13 @@ function registerBranchTools(server, http, options = {}) {
20833
20901
  responseTimeoutMs: 5000
20834
20902
  });
20835
20903
  if (!result.ok) {
20836
- 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);
20837
20905
  }
20838
20906
  const plan = parsePromotionPlan(result.data, { parentRef: ref, branchRef });
20839
20907
  if (result.status !== 200 || !plan) {
20840
- 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);
20841
20909
  }
20842
- 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) }] };
20843
20911
  } else if (action === "promote") {
20844
20912
  const branchRef = requireString(args.branch_ref, "branch_ref");
20845
20913
  const planChecksum = requireString(args.plan_checksum, "plan_checksum");
@@ -20858,18 +20926,18 @@ function registerBranchTools(server, http, options = {}) {
20858
20926
  });
20859
20927
  const beforeMutation = { ...state, mutation_sent: false };
20860
20928
  if (!preflight.ok) {
20861
- 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);
20862
20930
  }
20863
20931
  const plan = parsePromotionPlan(preflight.data, { parentRef: ref, branchRef });
20864
20932
  if (preflight.status !== 200 || !plan) {
20865
- 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);
20866
20934
  }
20867
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;
20868
20936
  if (preflightReason) {
20869
- 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);
20870
20938
  }
20871
20939
  if (plan.pending.length === 0) {
20872
- return args.json ? releaseControlSuccess("branch.promote", {
20940
+ return args.format === "json" ? releaseControlSuccess("branch.promote", {
20873
20941
  ...beforeMutation,
20874
20942
  promoted: false,
20875
20943
  unchanged: true,
@@ -20895,13 +20963,13 @@ function registerBranchTools(server, http, options = {}) {
20895
20963
  mutation_sent: true,
20896
20964
  reported_applied_versions: appliedVersions(result),
20897
20965
  ...safePromotionCode(result) ? { server_code: safePromotionCode(result) } : {}
20898
- }), 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);
20899
20967
  }
20900
20968
  const promoted = parsePromotionResult(result.data, { parentRef: ref, branchRef }, plan.pending);
20901
20969
  if (result.status !== 200 || !promoted) {
20902
- 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);
20903
20971
  }
20904
- return args.json ? releaseControlSuccess("branch.promote", {
20972
+ return args.format === "json" ? releaseControlSuccess("branch.promote", {
20905
20973
  ...promoted,
20906
20974
  unchanged: false,
20907
20975
  mutation_sent: true,
@@ -21574,7 +21642,8 @@ function buildLiteArgs(request) {
21574
21642
  booleanFlag(args, "--service-role", request.service_role);
21575
21643
  booleanFlag(args, "--force", request.force);
21576
21644
  booleanFlag(args, "--memory", request.memory);
21577
- booleanFlag(args, "--json", request.json);
21645
+ if (request.format === "json")
21646
+ args.push("--json");
21578
21647
  if (request.baseline && request.action !== "db_pull")
21579
21648
  throw new Error("baseline requires db_pull");
21580
21649
  booleanFlag(args, "--baseline", request.baseline);
@@ -21699,7 +21768,7 @@ function registerLiteCliTools(server, options = {}) {
21699
21768
  service_role: optional(Boolean2(), "[keys] Also print the service_role key"),
21700
21769
  force: optional(Boolean2(), "[snapshot_restore] Replace non-empty restore targets"),
21701
21770
  memory: optional(Boolean2(), "[*] Use an in-memory PGlite database"),
21702
- json: optional(Boolean2(), "[doctor] Emit machine-readable output"),
21771
+ format: optional(stringEnum2(["text", "json"]), "[doctor] Output format (default: text)"),
21703
21772
  baseline: optional(Boolean2(), "[db_pull] Record already-present schema in local migration history after review"),
21704
21773
  production: optional(Boolean2(), "[doctor] Add single-node production readiness checks"),
21705
21774
  runtime_mode: optional(stringEnum2(["development", "strict"]), "[*] Lite runtime mode"),
@@ -21858,6 +21927,26 @@ function resolveDefaultCodexSkillRoot(environment = process.env, homeDirectory =
21858
21927
  const codexHome = environment.CODEX_HOME?.trim();
21859
21928
  return join8(resolve12(codexHome || join8(homeDirectory, ".codex")), "skills");
21860
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
+ }
21861
21950
  function resolveBundledSkillDirectory(moduleUrl = import.meta.url) {
21862
21951
  const moduleDirectory = dirname5(fileURLToPath(moduleUrl));
21863
21952
  const candidates = [
@@ -21875,7 +21964,7 @@ function textResponse(payload) {
21875
21964
  function registerAiTools(server) {
21876
21965
  registerTool(server, "ai", "Install or inspect the bundled SupaCloud CLI AI skill. Actions: show_skill, install_skill", {
21877
21966
  action: stringEnum2(["show_skill", "install_skill"]),
21878
- 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"),
21879
21968
  dry_run: optional(Boolean2(), "[install_skill] Preview the installation without writing files"),
21880
21969
  force: optional(Boolean2(), "[install_skill] Back up and replace different existing skill content")
21881
21970
  }, async (request) => {
@@ -21886,12 +21975,17 @@ function registerAiTools(server) {
21886
21975
  name: SKILL_NAME,
21887
21976
  sourceDirectory,
21888
21977
  defaultTargetRoot,
21889
- 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
+ ]))
21890
21983
  });
21891
21984
  }
21985
+ const targetRoot = resolveSkillTargetRoot(request.target);
21892
21986
  return textResponse(installSkill({
21893
21987
  sourceDirectory,
21894
- targetRoot: request.target || defaultTargetRoot,
21988
+ targetRoot,
21895
21989
  mode: request.dry_run ? "dry-run" : "write",
21896
21990
  conflictPolicy: request.force ? "replace" : "reject",
21897
21991
  now: new Date
@@ -22091,16 +22185,16 @@ var starter_metadata_default = {
22091
22185
  generatedBy: "supacloud/starter-metadata-v1",
22092
22186
  packages: {
22093
22187
  compiler: {
22094
- version: "0.53.1"
22188
+ version: "0.54.0"
22095
22189
  },
22096
22190
  app: {
22097
- version: "0.35.0",
22191
+ version: "0.36.0",
22098
22192
  dependencies: {
22099
22193
  rxjs: "7.8.2"
22100
22194
  }
22101
22195
  },
22102
22196
  elysia: {
22103
- version: "0.37.0"
22197
+ version: "0.38.0"
22104
22198
  },
22105
22199
  commands: {
22106
22200
  version: "0.11.2"
@@ -22109,7 +22203,7 @@ var starter_metadata_default = {
22109
22203
  version: "0.15.2"
22110
22204
  },
22111
22205
  db: {
22112
- version: "0.20.3",
22206
+ version: "0.20.4",
22113
22207
  peerDependencies: {
22114
22208
  "drizzle-orm": "1.0.0-rc.5-5935859"
22115
22209
  },
@@ -22195,6 +22289,10 @@ requirement and equivalent lifecycle tests.
22195
22289
  Bind subscriptions to an explicit owner and propagate cancellation to native I/O.
22196
22290
  Never automatically retry a write or turn observable delivery into durable ack.
22197
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.
22198
22296
  Run bun run check; include negative and cleanup tests. Never store live credentials.
22199
22297
  `;
22200
22298
  var STARTER_REACTIVE_TEST = `import { expect, test } from "bun:test";
@@ -22436,7 +22534,6 @@ export function createCompiledModules(): never {
22436
22534
  }
22437
22535
  `;
22438
22536
  function baseFiles(name, sdkDependencies) {
22439
- const minimal = sdkDependencies !== undefined;
22440
22537
  return {
22441
22538
  "package.json": json({
22442
22539
  name,
@@ -22462,8 +22559,8 @@ function baseFiles(name, sdkDependencies) {
22462
22559
  "@supacloud/app": `^${appMetadata.version}`,
22463
22560
  "@supacloud/elysia": `^${elysiaMetadata.version}`,
22464
22561
  effect: "4.0.5",
22465
- elysia: "2.0.0-rc.0",
22466
- ...sdkDependencies ?? { rxjs: appMetadata.dependencies.rxjs }
22562
+ elysia: "2.0.0-rc.1",
22563
+ ...sdkDependencies
22467
22564
  },
22468
22565
  devDependencies: {
22469
22566
  "@supacloud/compiler": `^${compilerMetadata.version}`,
@@ -22508,253 +22605,16 @@ PORT=3000
22508
22605
  `,
22509
22606
  "supacloud.config.ts": `import { defineSupacloudConfig } from "@supacloud/compiler";
22510
22607
 
22511
- export default defineSupacloudConfig({
22512
- root: "src",
22513
- outDir: "generated",
22514
- strict: true,
22515
- moduleBoundaryPreset: "modular-monolith",
22516
- effect: {
22517
- requireRouteEffects: true,
22518
- requireErrorMappings: true,
22519
- requireDependencies: true,
22520
- requireTaggedErrorTypes: true,
22521
- requireExactDependencyTypes: true,
22522
- requireTimeoutForDependencies: true,
22523
- forbidDirectRuntimeExecution: true,
22524
- forbidDirectThrows: true,
22525
- },
22526
- typeSafety: { scanProductionSource: true, noAnyInGenerated: true },
22527
- disallowControllerDirectDb: true,
22528
- detectOrphanModules: true,
22529
- commandCapabilities: { permission: true, transaction: true, idempotency: true, audit: true },
22530
- });
22608
+ export default defineSupacloudConfig();
22531
22609
  `,
22532
22610
  "src/application.ts": APPLICATION_SOURCE(name),
22533
22611
  "generated/application.ts": GENERATED_BOOTSTRAP,
22534
- ...minimal ? {} : {
22535
- "REACTIVE.md": STARTER_REACTIVE_GUIDE,
22536
- "AGENTS.md": STARTER_REACTIVE_AGENTS,
22537
- "tests/reactive.test.ts": STARTER_REACTIVE_TEST
22538
- },
22539
22612
  "scripts/environment.ts": STARTER_ENVIRONMENT,
22540
22613
  "tests/environment.test.ts": STARTER_ENVIRONMENT_TEST,
22541
22614
  "scripts/dev.ts": DEV_SCRIPT,
22542
22615
  "scripts/serve.ts": SERVE_SCRIPT
22543
22616
  };
22544
22617
  }
22545
- function httpTemplate(name) {
22546
- return {
22547
- ...baseFiles(name),
22548
- "src/orders/contracts.ts": `import { t } from "elysia";
22549
- export const OrderParams = t.Object({ id: t.String({ minLength: 1 }) });
22550
- export const CreateOrderBody = t.Object({ name: t.String({ minLength: 1 }) });
22551
- export const OrderResult = t.Object({ id: t.String(), name: t.String() });
22552
- export const HealthResult = t.Object({ ok: t.Boolean() });
22553
- `,
22554
- "src/orders/orders.ts": `import { Effect } from "effect";
22555
- import { Body, Controller, Get, Param, Post, defineFeatureSlice } from "@supacloud/app";
22556
- import { status } from "elysia";
22557
- import { OrderParams, CreateOrderBody, OrderResult, HealthResult } from "./contracts";
22558
- @Controller("/orders")
22559
- export class OrdersController {
22560
- @Get("/health", {
22561
- responses: { 200: HealthResult },
22562
- effect: { required: true, dependencies: [], errors: [], retry: "none" },
22563
- })
22564
- health(): Effect.Effect<{ ok: boolean }, never, never> { return Effect.succeed({ ok: true }); }
22565
-
22566
- @Get("/:id", {
22567
- params: OrderParams,
22568
- responses: { 200: OrderResult },
22569
- effect: { required: true, dependencies: [], errors: [], retry: "none" },
22570
- })
22571
- get(@Param("id") id: string): Effect.Effect<{ id: string; name: string }, never, never> {
22572
- return Effect.succeed({ id, name: "demo" });
22573
- }
22574
-
22575
- @Post("/", {
22576
- body: CreateOrderBody,
22577
- responses: { 201: OrderResult },
22578
- effect: { required: true, dependencies: [], errors: [], retry: "none" },
22579
- })
22580
- create(@Body() body: { name: string }) {
22581
- return Effect.succeed(status(201, { id: "demo", name: body.name }));
22582
- }
22583
- }
22584
-
22585
- export const OrdersFeature = defineFeatureSlice({
22586
- name: "orders",
22587
- tags: ["type:feature"],
22588
- controllers: [OrdersController],
22589
- });
22590
- `,
22591
- "scripts/sandbox.ts": `import { createMemorySandbox } from "@supacloud/elysia";
22592
- import { createCompiledModules } from "../generated/application";
22593
-
22594
- export function createDemo() {
22595
- if (process.env["APP_ENV"] !== "development" && process.env["APP_ENV"] !== "test") {
22596
- throw new Error("Memory adapters are restricted to development and test");
22597
- }
22598
- return createMemorySandbox({
22599
- modules: createCompiledModules(),
22600
- identity: { authenticated: true, subject: "local-demo" },
22601
- });
22602
- }
22603
- `,
22604
- "tests/orders.test.ts": `import { expect, test } from "bun:test";
22605
- import { createDemo } from "../scripts/sandbox";
22606
-
22607
- test("health, read and create routes follow the declared HTTP contract", async () => {
22608
- const sandbox = createDemo();
22609
- expect((await sandbox.request("/orders/health")).status).toBe(200);
22610
- expect((await sandbox.request("/orders/1")).status).toBe(200);
22611
- const created = await sandbox.request("/orders", {
22612
- method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ name: "first" }),
22613
- });
22614
- expect(created.status).toBe(201);
22615
- expect(await created.json()).toEqual({ id: "demo", name: "first" });
22616
- const invalid = await sandbox.request("/orders", {
22617
- method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({}),
22618
- });
22619
- expect(invalid.status).toBe(422);
22620
- });
22621
- `,
22622
- "README.md": `# ${name}
22623
-
22624
- HTTP API golden path.
22625
-
22626
- ## Local Development
22627
-
22628
- \`\`\`sh
22629
- bun install
22630
- bun run check
22631
- bun run dev
22632
- \`\`\`
22633
-
22634
- The server binds only to 127.0.0.1:3000 by default.
22635
-
22636
- \`\`\`sh
22637
- curl http://127.0.0.1:3000/orders/health
22638
- curl http://127.0.0.1:3000/orders/1
22639
- curl -X POST http://127.0.0.1:3000/orders \\
22640
- -H 'Content-Type: application/json' -d '{"name":"first"}'
22641
- \`\`\`
22642
-
22643
- The generated feature declares explicit route schemas so the compiler can detect
22644
- route/schema drift. Keep request and response shapes in \`src/orders/orders.ts\`;
22645
- do not widen them to \`unknown\` just to accept unvalidated data.
22646
-
22647
- ## Production Boundary
22648
-
22649
- \`bun run build\` emits an application factory, not a server. Import \`createApp\`
22650
- from \`output/supacloud/application.js\` in your trusted host and supply real dependency,
22651
- request-context and command-governance adapters. The memory sandbox is
22652
- development/test only.
22653
-
22654
- Keep backend bundles outside the frontend output directory. Vite normally cleans
22655
- \`dist/\` before building; server artifacts placed there can be deleted or included
22656
- in a public frontend archive. Use disjoint output directories, not a disabled
22657
- frontend clean step. Compiler source artifacts remain in \`generated/\`.
22658
-
22659
- ## Inspection And Repair
22660
-
22661
- \`\`\`sh
22662
- supacloud context --root . --target orders --format json
22663
- supacloud doctor --root .
22664
- \`\`\`
22665
- `
22666
- };
22667
- }
22668
- function edgeTemplate(name) {
22669
- return {
22670
- ...baseFiles(name),
22671
- "src/sync/sync.ts": `import { Injectable, Job, defineFeatureSlice } from "@supacloud/app";
22672
-
22673
- /**
22674
- * Worker/edge golden path: a declared, retry-bounded job with an explicit
22675
- * idempotency requirement. The platform owns scheduling, retry and DLQ; the
22676
- * handler stays side-effect explicit and must not assume exactly-once execution.
22677
- */
22678
- @Injectable()
22679
- @Job({
22680
- name: "sync.orders",
22681
- mode: "task",
22682
- idempotency: "required",
22683
- timeoutSec: 60,
22684
- maxAttempts: 3,
22685
- })
22686
- export class SyncOrdersJob {
22687
- async run(): Promise<{ ok: boolean }> {
22688
- return { ok: true };
22689
- }
22690
- }
22691
-
22692
- export const SyncFeature = defineFeatureSlice({
22693
- name: "sync",
22694
- tags: ["type:feature"],
22695
- providers: [SyncOrdersJob],
22696
- jobs: [SyncOrdersJob],
22697
- });
22698
- `,
22699
- "scripts/sandbox.ts": `import { createMemorySandbox } from "@supacloud/elysia";
22700
- import { createCompiledModules } from "../generated/application";
22701
-
22702
- export function createDemo() {
22703
- if (process.env["APP_ENV"] !== "development" && process.env["APP_ENV"] !== "test") {
22704
- throw new Error("Memory adapters are restricted to development and test");
22705
- }
22706
- return createMemorySandbox({
22707
- modules: createCompiledModules(),
22708
- identity: { authenticated: true, subject: "local-worker" },
22709
- });
22710
- }
22711
- `,
22712
- "tests/sync.test.ts": `import { expect, test } from "bun:test";
22713
- import { SyncOrdersJob } from "../src/sync/sync";
22714
-
22715
- test("the declared job handler is deterministic and idempotent by construction", async () => {
22716
- const job = new SyncOrdersJob();
22717
- expect(await job.run()).toEqual({ ok: true });
22718
- expect(await job.run()).toEqual({ ok: true });
22719
- });
22720
- `,
22721
- "README.md": `# ${name}
22722
-
22723
- Worker / Edge golden path.
22724
-
22725
- ## Local Development
22726
-
22727
- \`\`\`sh
22728
- bun install
22729
- bun run check
22730
- bun run test
22731
- \`\`\`
22732
-
22733
- \`src/sync/sync.ts\` declares a platform job with an explicit mode, timeout,
22734
- attempt bound and idempotency requirement. The platform schedules and retries;
22735
- the handler must be safe to run more than once and must not assume exactly-once
22736
- execution.
22737
-
22738
- ## Production Boundary
22739
-
22740
- The job runs through the platform worker/edge adapter, not the local memory
22741
- sandbox. Bind persistence, authorization and audit to durable adapters before
22742
- invoking it in production. Do not schedule destructive work without an
22743
- idempotency key.
22744
-
22745
- \`bun run build\` emits \`output/supacloud/application.js\`, an application factory,
22746
- not a deployed Worker. Keep it outside any frontend output directory such as
22747
- \`dist/\`; frontend clean steps and public archives must not own server bundles.
22748
-
22749
- ## Inspection And Repair
22750
-
22751
- \`\`\`sh
22752
- supacloud context --root . --target sync --format json
22753
- supacloud doctor --root .
22754
- \`\`\`
22755
- `
22756
- };
22757
- }
22758
22618
  function minimalTemplate(name, sdkDependencies) {
22759
22619
  return {
22760
22620
  ...baseFiles(name, sdkDependencies),
@@ -22809,8 +22669,12 @@ export function createDemo() {
22809
22669
  - Cross-table business writes require trusted authorization, transactions,
22810
22670
  idempotency and audit. Never put service-role or Management tokens in a browser.
22811
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.
22812
22676
  - Run the directly related single test file and git diff --check for daily changes.
22813
- - 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.
22814
22678
  - Do not introduce another workflow engine, DI container or session store.
22815
22679
  `,
22816
22680
  "README.md": `# ${name}
@@ -22844,8 +22708,8 @@ Use fixed trusted HTTPS endpoints and never retry an uncertain write.
22844
22708
 
22845
22709
  GraphQL is optional; this starter has no synthetic GraphQL schema.
22846
22710
  For a complete persistent command/attachment example, create a separate reference
22847
- project with app init --template command. The http and edge recipes also remain
22848
- 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.
22849
22713
 
22850
22714
  ## Integration and delivery
22851
22715
 
@@ -22862,58 +22726,9 @@ regeneration, then validate types, database permissions and authenticated behavi
22862
22726
  };
22863
22727
  }
22864
22728
  function appTemplateFiles(name, template, sdkDependencies) {
22865
- if (template === "minimal") {
22866
- if (!sdkDependencies)
22867
- throw new Error("Minimal template requires the initializer's SDK dependency versions");
22868
- return minimalTemplate(name, sdkDependencies);
22869
- }
22870
- if (template !== "http" && template !== "edge")
22729
+ if (template !== "minimal")
22871
22730
  throw new Error("Use appStarterFiles for the command recipe");
22872
- const files = template === "http" ? httpTemplate(name) : edgeTemplate(name);
22873
- const feature = template === "http" ? "OrdersFeature" : "SyncFeature";
22874
- const source = template === "http" ? "./orders/orders" : "./sync/sync";
22875
- return {
22876
- ...files,
22877
- "src/app.module.ts": `import { Module } from "@supacloud/app";
22878
- import { ${feature} } from "${source}";
22879
-
22880
- // Compose feature modules here. Do not make unrelated features import each other.
22881
- @Module({
22882
- name: "application-root",
22883
- tags: ["type:app"],
22884
- imports: [${feature}],
22885
- })
22886
- export class AppModule {}
22887
- `,
22888
- "README.md": files["README.md"] + `
22889
- ## Application Composition
22890
-
22891
- \`src/app.module.ts\` is the application composition root. Register additional
22892
- feature modules there, rather than importing them from another feature.
22893
- The compiler discovers this declaration; no runtime container lookup is needed.
22894
-
22895
- With the matching candidate/released CLI installed:
22896
-
22897
- \`\`\`sh
22898
- supacloud app generate --kind resource --name inventory --register-in src/app.module.ts --dry-run --format json
22899
- supacloud app generate --kind resource --name inventory --register-in src/app.module.ts
22900
- bun run check
22901
- bun run inspect
22902
- \`\`\`
22903
-
22904
- The resource is an asynchronous read skeleton, not implemented CRUD. Its Service
22905
- rejects until an authorized data adapter is supplied; implement it and replace the
22906
- placeholder test together. Generation does not grant access or connect a database.
22907
-
22908
- \`bun run inspect\` delegates to the installed compiler and prints the current
22909
- source graph as JSON without starting the app or regenerating artifacts. This is
22910
- not a live mounted-route report. Configuration remains trusted project code.
22911
- After changing source, compile before using artifact-backed CLI graph/explain.
22912
-
22913
- In CI, run \`bun run check:generated\` before commands that regenerate artifacts;
22914
- \`bun run check\` is the convenient bootstrap path for a newly created project.
22915
- `
22916
- };
22731
+ return minimalTemplate(name, sdkDependencies);
22917
22732
  }
22918
22733
 
22919
22734
  // src/shared/tools/app-starter-review-job.ts
@@ -24214,9 +24029,9 @@ After installing dependencies, run \`bun run db:generate\` once and commit db/co
24214
24029
  The default \`bun run check\` and build fail on stale database contracts before any
24215
24030
  compiler regeneration. After an intentional source change, run db:generate again,
24216
24031
  review the diff, then run check.
24217
- The command starter registers databaseGovernance.moduleFile = "db/modules.ts" and
24218
- keeps the default \`databaseGovernance.mode = "check"\`: compile/check/build run
24219
- 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\`
24220
24035
  with the CLI's explicit loopback database URL, or set databaseGovernance.mode to
24221
24036
  "enforce" with an explicitly supplied \`DATABASE_URL\`, to run SQL lint and live
24222
24037
  Catalog reconciliation. Missing connections, stale source contracts and Catalog
@@ -25628,7 +25443,7 @@ function appStarterFiles(name) {
25628
25443
  "@supacloud/js": `^${sdkMetadata.version}`,
25629
25444
  "@supabase/supabase-js": sdkMetadata.peerDependencies["@supabase/supabase-js"],
25630
25445
  effect: "4.0.5",
25631
- elysia: "2.0.0-rc.0",
25446
+ elysia: "2.0.0-rc.1",
25632
25447
  rxjs: appMetadata2.dependencies.rxjs
25633
25448
  },
25634
25449
  devDependencies: {
@@ -25687,25 +25502,7 @@ PORT=3000
25687
25502
  "supacloud.config.ts": `import { defineSupacloudConfig } from "@supacloud/compiler";
25688
25503
 
25689
25504
  export default defineSupacloudConfig({
25690
- root: "src",
25691
- outDir: "generated",
25692
- strict: true,
25693
- moduleBoundaryPreset: "modular-monolith",
25694
- databaseGovernance: { mode: "check", moduleFile: "db/modules.ts", databaseUrlEnv: "DATABASE_URL" },
25695
- effect: {
25696
- requireRouteEffects: true,
25697
- requireErrorMappings: true,
25698
- requireDependencies: true,
25699
- requireTaggedErrorTypes: true,
25700
- requireExactDependencyTypes: true,
25701
- requireTimeoutForDependencies: true,
25702
- forbidDirectRuntimeExecution: true,
25703
- forbidDirectThrows: true,
25704
- },
25705
- typeSafety: { scanProductionSource: true, noAnyInGenerated: true },
25706
- disallowControllerDirectDb: true,
25707
- detectOrphanModules: true,
25708
- commandCapabilities: { permission: true, transaction: true, idempotency: true, audit: true },
25505
+ databaseGovernance: { mode: "check" },
25709
25506
  delivery: {
25710
25507
  version: 1,
25711
25508
  build: {
@@ -26496,7 +26293,7 @@ and static execution plans, without provider values or source expressions.
26496
26293
  The app context entry point retains its module workflow and adds a separate
26497
26294
  execution evidence block when --events and --request-id are provided together.
26498
26295
  It requires an explicit --target and --format json, rejects --write, and
26499
- resolves relative event paths from --root. The top-level context alias supports
26296
+ resolves relative event paths from --root. The app context action supports
26500
26297
  the same flags. Omit both execution flags for the unchanged authoring context.
26501
26298
  The optional --events example expects a trusted host
26502
26299
  to capture approved onExecution metadata in { "version": 1, "events": [...] }.
@@ -28255,13 +28052,27 @@ async function initProject(args) {
28255
28052
  throw new Error("app init never overwrites files; choose an empty directory");
28256
28053
  const template = args.template ?? "minimal";
28257
28054
  const { root, name, files } = await initializeAppProject({ root: args.root, name: args.name, template });
28258
- return textResult([
28259
- `Initialized ${name} in ${root} (${files.length} files, template: ${template}).`,
28260
- `cd '${root.replaceAll("'", "'\\''")}'`,
28055
+ const next = [
28261
28056
  "bun install",
28262
28057
  ...template === "command" ? ["bun run db:generate"] : [],
28263
28058
  "bun run check",
28264
- "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,
28265
28076
  "Local demo only. Production requires persistent governance and identity adapters; see README.md."
28266
28077
  ].join(`
28267
28078
  `));
@@ -28660,9 +28471,9 @@ async function runContext(args) {
28660
28471
  `));
28661
28472
  }
28662
28473
  function doctorFixPlan(doctor) {
28663
- return createDiagnosticRepairPlan(doctor.diagnostics ?? []).map((repair) => ({
28474
+ return createDiagnosticRepairPlan(doctor.diagnostics ?? []).map((repair, index) => ({
28664
28475
  ...repair,
28665
- command: "supacloud app fix --fix <fix.json>"
28476
+ command: `supacloud app fix --from-doctor <doctor.json> --fix-index ${index}`
28666
28477
  }));
28667
28478
  }
28668
28479
  async function findUnwiredContracts(rootDir) {
@@ -28718,6 +28529,11 @@ async function runDoctor(args) {
28718
28529
  const autoFixable = fixPlan.filter((repair) => repair.readiness === "preview").length;
28719
28530
  const inputRequired = fixPlan.filter((repair) => repair.readiness === "input-required").length;
28720
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
+ ];
28721
28537
  const unwiredContracts = context ? [] : await findUnwiredContracts(root);
28722
28538
  const scope = context ? {
28723
28539
  target: context.subject,
@@ -28729,6 +28545,7 @@ async function runDoctor(args) {
28729
28545
  if (args.format === "json") {
28730
28546
  return textResult(JSON.stringify({
28731
28547
  ok: doctor.errors === 0,
28548
+ next,
28732
28549
  autoFixable,
28733
28550
  inputRequired,
28734
28551
  manualFixes,
@@ -28747,7 +28564,7 @@ async function runDoctor(args) {
28747
28564
  lines.push("");
28748
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.`);
28749
28566
  if (autoFixable > 0) {
28750
- 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.`);
28751
28568
  }
28752
28569
  if (inputRequired > 0 || manualFixes > 0) {
28753
28570
  lines.push(`${inputRequired} fix(es) require explicit input; ${manualFixes} require manual implementation.`);
@@ -28764,13 +28581,49 @@ async function runDoctor(args) {
28764
28581
  async function runFix(args) {
28765
28582
  const root = resolve18(args.root || process.cwd());
28766
28583
  const fixPath = args.fix?.trim();
28767
- if (!fixPath)
28768
- throw new Error("app fix requires --fix <path-to-diagnostic-fix.json>");
28769
- 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
+ }
28770
28618
  const write = args.write === true;
28771
28619
  const applied = await applyDiagnosticFix(parsed, { rootDir: root, dryRun: !write });
28772
28620
  return textResult(JSON.stringify({
28773
28621
  ok: true,
28622
+ source,
28623
+ ...source === "doctor" ? {
28624
+ index: args.fix_index,
28625
+ ...repair?.code === undefined ? {} : { code: repair.code }
28626
+ } : {},
28774
28627
  written: write,
28775
28628
  file: applied.file,
28776
28629
  changed: applied.changed,
@@ -28990,11 +28843,14 @@ async function runCheck(args) {
28990
28843
  strict: args.strict ?? loadedConfig.strict ?? false
28991
28844
  }, root);
28992
28845
  const result = await checkProject(config);
28993
- 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>"];
28994
28849
  if (args.format === "json")
28995
28850
  return textResult(JSON.stringify({
28996
28851
  version: 1,
28997
28852
  ok: !hasError,
28853
+ next,
28998
28854
  source: "declaration",
28999
28855
  diagnostics: result.diagnostics,
29000
28856
  mismatches: result.mismatches,
@@ -29420,7 +29276,7 @@ async function runAppRelease(request, options, delegate) {
29420
29276
  ...typeof deployReceipt.activation_id === "string" ? { activation_id: deployReceipt.activation_id } : {},
29421
29277
  ...deployReceipt.web === undefined ? {} : { web: deployReceipt.web }
29422
29278
  };
29423
- 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([
29424
29280
  `${ref}/${id}/${environmentId}: ${deployReceipt.status === "accepted" ? "accepted" : result.no_op ? "no-op" : "released"}`,
29425
29281
  `Release: ${releaseId}`,
29426
29282
  `Configuration: ${configurationId}`,
@@ -29623,7 +29479,7 @@ async function runPromotion(request, options, delegate) {
29623
29479
  const ref = request.ref ?? options.projectRef;
29624
29480
  if (request.mutation_id !== undefined)
29625
29481
  throw new Error("app promote generates a new mutation ID; use promote-status or promote-reconcile to inspect an existing mutation");
29626
- const { format, json, ...remoteRequest } = request;
29482
+ const { format, ...remoteRequest } = request;
29627
29483
  const planArgs = {
29628
29484
  ...remoteRequest,
29629
29485
  action: "get_promotion_plan",
@@ -29661,7 +29517,7 @@ async function runPromotion(request, options, delegate) {
29661
29517
  no_op: true,
29662
29518
  execution_performed: false
29663
29519
  });
29664
- 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
29665
29521
  Execution: skipped`);
29666
29522
  }
29667
29523
  if (plan.blockers.some((code) => !["MIGRATION_PENDING", "BACKUP_REQUIRED"].includes(code))) {
@@ -29683,23 +29539,9 @@ Execution: skipped`);
29683
29539
  };
29684
29540
  validateToolArguments2(APPLICATION_TOOL_SCHEMA2, executeArgs);
29685
29541
  const result = await delegate(executeArgs);
29686
- return format === "json" || json ? result : formatPromotionExecution(result);
29542
+ return format === "json" ? result : formatPromotionExecution(result);
29687
29543
  }
29688
29544
  async function runAppTool(request, options = {}) {
29689
- 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) {
29690
- throw new ScaffoldError("SCAFFOLD_OPTION_INVALID", "Do not combine hyphenated and underscored aliases of the same flag");
29691
- }
29692
- request = {
29693
- ...request,
29694
- ...request["dry-run"] === undefined ? {} : { dry_run: request["dry-run"] },
29695
- ...request["register-in"] === undefined ? {} : { register_in: request["register-in"] },
29696
- ...request["database-url"] === undefined ? {} : { database_url: request["database-url"] },
29697
- ...request["database-source"] === undefined ? {} : { database_source: request["database-source"] },
29698
- ...request["database-table"] === undefined ? {} : { database_table: request["database-table"] },
29699
- ...request["database-type"] === undefined ? {} : { database_type: request["database-type"] },
29700
- ...request["read-permission"] === undefined ? {} : { read_permission: request["read-permission"] },
29701
- ...request["request-id"] === undefined ? {} : { request_id: request["request-id"] }
29702
- };
29703
29545
  if (request.events !== undefined || request.request_id !== undefined) {
29704
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) {
29705
29547
  throw new Error("Execution context requires app context, --target, --events, --request-id and --format json; --write is not supported");
@@ -29711,10 +29553,6 @@ async function runAppTool(request, options = {}) {
29711
29553
  if (request.database_url !== undefined && request.action !== "dev" && request.action !== "watch") {
29712
29554
  throw new ScaffoldError("SCAFFOLD_OPTION_INVALID", "--database-url applies only to app dev --profile integration");
29713
29555
  }
29714
- if (request.json !== undefined && !Object.hasOwn(REMOTE_APP_ACTIONS, request.action) && request.action !== "promote" && request.action !== "release")
29715
- throw new Error("--json applies only to remote app actions");
29716
- if (request.json && request.format === "text")
29717
- throw new Error("--json and --format text cannot be combined");
29718
29556
  if (request.action === "promote") {
29719
29557
  const delegate = options.getApplications?.();
29720
29558
  if (!delegate)
@@ -29733,7 +29571,7 @@ async function runAppTool(request, options = {}) {
29733
29571
  return textResult("App remote actions require a Management API context.", true);
29734
29572
  const promotionDiff = request.action === "diff" && request.source_ref !== undefined && request.source_environment_id !== undefined && request.source_release_id !== undefined;
29735
29573
  const action = promotionDiff ? "get_promotion_plan" : REMOTE_APP_ACTIONS[request.action];
29736
- const { format, json, ...remoteRequest } = request;
29574
+ const { format, ...remoteRequest } = request;
29737
29575
  const args = {
29738
29576
  ...remoteRequest,
29739
29577
  action,
@@ -29741,7 +29579,7 @@ async function runAppTool(request, options = {}) {
29741
29579
  };
29742
29580
  validateToolArguments2(APPLICATION_TOOL_SCHEMA2, args);
29743
29581
  const result = await delegate(args);
29744
- if (request.action === "evidence" && format !== "json" && json !== true && !result.isError) {
29582
+ if (request.action === "evidence" && format !== "json" && !result.isError) {
29745
29583
  try {
29746
29584
  const payload = JSON.parse(result.content[0].text);
29747
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))
@@ -29756,17 +29594,17 @@ async function runAppTool(request, options = {}) {
29756
29594
  return textResult("DEPLOYMENT unknown: invalid or mismatched evidence; no mutation was attempted.", true);
29757
29595
  }
29758
29596
  }
29759
- if (request.action === "history" && format !== "json" && json !== true) {
29597
+ if (request.action === "history" && format !== "json") {
29760
29598
  return historyText(result, request, args.ref);
29761
29599
  }
29762
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)) {
29763
29601
  return formatRemoteDeployPlan(result);
29764
29602
  }
29765
29603
  if (request.action === "promote-plan" || request.action === "diff" && request.source_ref !== undefined && request.source_environment_id !== undefined && request.source_release_id !== undefined) {
29766
- if (!result.isError && format !== "json" && !json)
29604
+ if (!result.isError && format !== "json")
29767
29605
  return formatPromotionPlan2(result);
29768
29606
  }
29769
- 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;
29770
29608
  }
29771
29609
  switch (request.action) {
29772
29610
  case "verify-plan": {
@@ -29868,83 +29706,38 @@ function registerAppTools(server, options = {}) {
29868
29706
  "promote-reconcile"
29869
29707
  ]), "App action; promote obtains and verifies its plan before one mutation request"),
29870
29708
  kind: optional(stringEnum2(["module", "command", "query", "controller", "job", "contract", "resource", "feature"]), "[generate] Scaffold kind"),
29871
- 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"),
29872
29710
  name: optional(String2(), "[init/generate] Project or object name"),
29873
29711
  module: optional(String2(), "[generate] Target feature module (required for command/query/controller)"),
29874
29712
  permission: optional(String2(), "[generate feature] Explicit approval permission; not inferred from name"),
29875
29713
  read_permission: optional(String2(), "[generate feature] Explicit read permission; not inferred from name"),
29876
- "read-permission": optional(String2(), "[generate feature] CLI alias of read_permission"),
29877
29714
  rpc: optional(String2(), "[generate feature] Explicit durable RPC capability; must be registered by the host"),
29878
29715
  database_source: optional(String2(), "[generate feature] Project-relative database source path"),
29879
29716
  database_table: optional(String2(), "[generate feature] Schema-qualified table owned by the feature"),
29880
29717
  database_type: optional(String2(), "[generate feature] Exported row type derived from the database source"),
29881
- "database-source": optional(String2(), "[generate feature] CLI alias of database_source"),
29882
- "database-table": optional(String2(), "[generate feature] CLI alias of database_table"),
29883
- "database-type": optional(String2(), "[generate feature] CLI alias of database_type"),
29884
29718
  dir: optional(String2(), "[generate] Feature root directory (default: src/features)"),
29885
29719
  force: optional(Boolean2(), "[generate] Overwrite a single regular file; resource/feature/controller never overwrite"),
29886
29720
  dry_run: optional(Boolean2(), "[generate] Validate and preview the complete change set without writing"),
29887
- "dry-run": optional(Boolean2(), "[generate] CLI alias of dry_run"),
29888
29721
  register_in: optional(String2(), "[generate] Existing parent module file for module/resource/feature; uses the compiler AST editor"),
29889
- "register-in": optional(String2(), "[generate] CLI alias of register_in"),
29890
29722
  root: optional(String2(), "[dev/plan/build/release/compile/check] Project directory containing supacloud.config.ts (default: current directory)"),
29891
29723
  include: optional(String2(), "[dev/compile/check] Comma-separated glob patterns for source files"),
29892
29724
  out_dir: optional(String2(), "[dev/compile/plan/build/export-tools] Output directory (default: configured outDir)"),
29893
29725
  strict: optional(Boolean2(), "[dev/compile/check] Promote warnings to errors"),
29894
- format: optional(stringEnum2(["text", "json"]), "[dev/generate/compile/check/plan/release/deploy-plan/diff/history/promote-plan/graph/export-tools] Output format (default: text)"),
29895
- 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)"),
29896
29727
  profile: optional(stringEnum2(["fast", "integration"]), "[dev] Run dev or dev:integration; integration verifies an explicit loopback database"),
29897
29728
  once: optional(Boolean2(), "[dev] Validate and report once without watching"),
29898
29729
  watch: optional(Boolean2(), "[dev] Watch for changes (default: true; --once disables)"),
29899
29730
  database_url: optional(String2(), "[dev] Explicit loopback integration URL; watch/once only inspect its shape"),
29900
- "database-url": optional(String2(), "[dev] CLI alias of database_url"),
29901
29731
  target: optional(String2(), "[explain/context/verify-plan] Provider class name / token name / command name / job name / module name"),
29902
29732
  events: optional(String2(), "[context] Project-relative execution metadata JSON or generated JSON-lines file"),
29903
29733
  request_id: optional(String2(), "[context] Opaque request ID or Workflow run ID selected from --events"),
29904
- "request-id": optional(String2(), "[context] CLI alias of request_id"),
29905
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"),
29906
29737
  write: optional(Boolean2(), "[fix] Write the fix to disk (default: preview only)"),
29907
29738
  skip_build: optional(Boolean2(), "[release] Reuse --manifest_path instead of running the local delivery build")
29908
29739
  }, (request) => runAppTool(request, options));
29909
29740
  }
29910
- var APP_ALIAS_ACTIONS = ["generate", "compile", "check", "graph", "explain", "context", "doctor", "fix"];
29911
- function registerAppAliases(server) {
29912
- const schema = {
29913
- kind: optional(stringEnum2(["module", "command", "query", "controller", "job", "contract", "resource", "feature"]), "[generate] Scaffold kind"),
29914
- name: optional(String2(), "[generate] Object name"),
29915
- module: optional(String2(), "[generate] Target feature module"),
29916
- permission: optional(String2(), "[generate feature] Explicit approval permission; not inferred from name"),
29917
- read_permission: optional(String2(), "[generate feature] Explicit read permission; not inferred from name"),
29918
- "read-permission": optional(String2(), "[generate feature] CLI alias of read_permission"),
29919
- rpc: optional(String2(), "[generate feature] Explicit durable RPC capability; must be registered by the host"),
29920
- database_source: optional(String2(), "[generate feature] Project-relative database source path"),
29921
- database_table: optional(String2(), "[generate feature] Schema-qualified table owned by the feature"),
29922
- database_type: optional(String2(), "[generate feature] Exported row type derived from the database source"),
29923
- "database-source": optional(String2(), "[generate feature] CLI alias of database_source"),
29924
- "database-table": optional(String2(), "[generate feature] CLI alias of database_table"),
29925
- "database-type": optional(String2(), "[generate feature] CLI alias of database_type"),
29926
- dir: optional(String2(), "[generate] Feature root directory (default: src/features)"),
29927
- force: optional(Boolean2(), "[generate] Overwrite a single regular file; resource/feature/controller never overwrite"),
29928
- dry_run: optional(Boolean2(), "[generate] Validate and preview the complete change set without writing"),
29929
- "dry-run": optional(Boolean2(), "[generate] CLI alias of dry_run"),
29930
- register_in: optional(String2(), "[generate] Existing parent module file for module/resource/feature; uses the compiler AST editor"),
29931
- "register-in": optional(String2(), "[generate] CLI alias of register_in"),
29932
- root: optional(String2(), "Project directory containing supacloud.config.ts (default: current directory)"),
29933
- include: optional(String2(), "[compile/check] Comma-separated glob patterns for source files"),
29934
- out_dir: optional(String2(), "[compile/export-tools] Output directory (default: <root>/generated)"),
29935
- strict: optional(Boolean2(), "[compile/check] Promote warnings to errors"),
29936
- format: optional(stringEnum2(["text", "json"]), "Output format (default: text)"),
29937
- target: optional(String2(), "[explain/context] Provider class name / token name / command name / job name / module name"),
29938
- events: optional(String2(), "[context] Project-relative execution metadata JSON or generated JSON-lines file"),
29939
- request_id: optional(String2(), "[context] Opaque request ID or Workflow run ID selected from --events"),
29940
- "request-id": optional(String2(), "[context] CLI alias of request_id"),
29941
- fix: optional(String2(), "[fix] Path to a DiagnosticFix JSON file produced by `doctor --format json`"),
29942
- write: optional(Boolean2(), "[fix] Write the fix to disk (default: preview only)")
29943
- };
29944
- for (const action of APP_ALIAS_ACTIONS) {
29945
- registerTool(server, action, `Top-level alias of \`app ${action}\`.`, schema, (request) => runAppTool({ ...request, action }));
29946
- }
29947
- }
29948
29741
 
29949
29742
  // src/shared/tools/db-governance-tools.ts
29950
29743
  import { existsSync as existsSync13 } from "node:fs";
@@ -30387,7 +30180,7 @@ async function runOfficial(root, workdir, args, environment, options) {
30387
30180
  if (code !== 0)
30388
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.`);
30389
30182
  }
30390
- async function initializeCandidate(stage, majorVersion = 17) {
30183
+ async function initializeCandidate(stage, majorVersion = 18) {
30391
30184
  if (![15, 16, 17, 18].includes(majorVersion))
30392
30185
  throw new Error("Select a supported PostgreSQL major version (15-18)");
30393
30186
  await mkdir6(join15(stage, "supabase/migrations"), { recursive: true });
@@ -30814,12 +30607,71 @@ async function runModuleCheckLite(args, root, spawn, liteBinary) {
30814
30607
  `);
30815
30608
  return textResult2(output || "lite db check 无输出", result.exitCode !== 0);
30816
30609
  }
30817
- async function runModuleCheckAction(args, root, environment, spawn, liteBinary) {
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);
30818
30670
  if (args.lite)
30819
30671
  return runModuleCheckLite(args, root, spawn, liteBinary);
30820
30672
  const databaseUrl = args.database_url?.trim() || environment.DATABASE_URL?.trim();
30821
30673
  if (!databaseUrl) {
30822
- throw new Error("db module_check requires --database_url、环境变量 DATABASE_URL 或 --lite");
30674
+ throw new Error("db module_check requires --database_url、环境变量 DATABASE_URL、--lite 或 --remote");
30823
30675
  }
30824
30676
  const modules = await loadDatabaseModules(root, args.module_file);
30825
30677
  const schemas = parseSchemas(args.schema);
@@ -30857,12 +30709,13 @@ function registerDbGovernanceTools(server, options = {}) {
30857
30709
  database_url: optional(String2(), "[module_check] Postgres connection URL (default: DATABASE_URL)"),
30858
30710
  schema: optional(String2(), "[module_check] Comma-separated schemas to inspect (default: public)"),
30859
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"),
30860
30713
  project_dir: optional(String2(), "[module_check] Lite project directory (with --lite; default: --root)"),
30861
30714
  schema_dir: optional(String2(), "[diff] Maintained declarative SQL directory (default: supabase/schemas)"),
30862
- 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"),
30863
30716
  out: optional(String2(), "[reverse/diff] Candidate output path (reverse requires a new directory)"),
30864
30717
  dir: optional(String2(), "[diff/plan/apply] Reviewed forward migration directory"),
30865
- 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)"),
30866
30719
  approved_digest: optional(String2(), "[apply] Exact digest returned by db plan"),
30867
30720
  application_role: optional(String2(), "[role_sql] Application database role"),
30868
30721
  migration_role: optional(String2(), "[role_sql] Privileged migration role"),
@@ -30877,12 +30730,20 @@ function registerDbGovernanceTools(server, options = {}) {
30877
30730
  case "explain":
30878
30731
  return runExplain2(request, root);
30879
30732
  case "module_check":
30880
- 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
+ });
30881
30743
  case "reverse":
30882
30744
  case "diff":
30883
30745
  case "plan":
30884
30746
  case "apply":
30885
- case "role_check":
30886
30747
  case "role_sql":
30887
30748
  return runDbWorkflow({ ...request, action: request.action }, root, {
30888
30749
  environment,
@@ -32222,11 +32083,11 @@ function registerReleaseTools(server, http, options = {}) {
32222
32083
  }
32223
32084
 
32224
32085
  // src/shared/tools/deploy-tools.ts
32225
- 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";
32226
32087
  import { spawn as spawn2 } from "node:child_process";
32227
32088
  import { createHash as createHash11 } from "node:crypto";
32228
32089
  import { tmpdir as tmpdir3 } from "node:os";
32229
- 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";
32230
32091
 
32231
32092
  // src/shared/tools/project-deployment.ts
32232
32093
  async function requireProjectDeploymentProtocol(http, projectRef, environmentId) {
@@ -32301,26 +32162,387 @@ async function readProjectDeploymentStatus(http, projectRef, environmentId, depl
32301
32162
  }
32302
32163
  }
32303
32164
 
32304
- // src/shared/tools/deploy-tools.ts
32305
- var MAX_SOURCE_BYTES = 256 * 1024 * 1024;
32306
- var PUBLIC_BUILD_ENV_PREFIXES = ["PUBLIC_", "VITE_", "NEXT_PUBLIC_", "NUXT_PUBLIC_", "ASTRO_PUBLIC_"];
32307
- var SENSITIVE_BUILD_ENV = /(?:^|_)(?:PASSWORD|PASS|SECRET|TOKEN|KEY|CREDENTIAL|AUTHORIZATION|COOKIE|SESSION)(?:_|$)|(?:DATABASE_URL|DATABASE_URI|CONNECTION_STRING|DSN|PRIVATE_KEY)/i;
32308
- function isPublicBuildEnv(name) {
32309
- return PUBLIC_BUILD_ENV_PREFIXES.some((prefix) => name.startsWith(prefix));
32310
- }
32311
- function isProtectedBuildEnv(name) {
32312
- return name.startsWith("SUPACLOUD_") || name === "SUPABASE_SERVICE_ROLE_KEY" || name === "SUPABASE_ACCESS_TOKEN" || name === "MANAGEMENT_API_URL" || name === "MANAGEMENT_API_TOKEN";
32313
- }
32314
- var deployToolSchema = {
32315
- ref: optional(String2(), "Project ref (defaults to the linked project)"),
32316
- cwd: optional(String2(), "Project directory (default: current directory)"),
32317
- skip_build: optional(Boolean2(), "Use an existing output directory without building"),
32318
- assets_only: optional(Boolean2(), "Publish only configured static assets; leave backend Functions, schedules and migrations unchanged"),
32319
- backend: optional(Boolean2(), "Publish the application backend independently; do not build or activate frontend assets"),
32320
- status: optional(String2({ format: "uuid" }), "Read a backend deployment status without publishing"),
32321
- json: optional(Boolean2(), "Print only the final JSON result")
32322
- };
32323
- function record5(candidate) {
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
+
32523
+ // src/shared/tools/deploy-tools.ts
32524
+ var MAX_SOURCE_BYTES = 256 * 1024 * 1024;
32525
+ var PUBLIC_BUILD_ENV_PREFIXES = ["PUBLIC_", "VITE_", "NEXT_PUBLIC_", "NUXT_PUBLIC_", "ASTRO_PUBLIC_"];
32526
+ var SENSITIVE_BUILD_ENV = /(?:^|_)(?:PASSWORD|PASS|SECRET|TOKEN|KEY|CREDENTIAL|AUTHORIZATION|COOKIE|SESSION)(?:_|$)|(?:DATABASE_URL|DATABASE_URI|CONNECTION_STRING|DSN|PRIVATE_KEY)/i;
32527
+ function isPublicBuildEnv(name) {
32528
+ return PUBLIC_BUILD_ENV_PREFIXES.some((prefix) => name.startsWith(prefix));
32529
+ }
32530
+ function isProtectedBuildEnv(name) {
32531
+ return name.startsWith("SUPACLOUD_") || name === "SUPABASE_SERVICE_ROLE_KEY" || name === "SUPABASE_ACCESS_TOKEN" || name === "MANAGEMENT_API_TOKEN";
32532
+ }
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)"),
32537
+ ref: optional(String2(), "Project ref (defaults to the linked project)"),
32538
+ cwd: optional(String2(), "Project directory (default: current directory)"),
32539
+ skip_build: optional(Boolean2(), "Use an existing output directory without building"),
32540
+ assets_only: optional(Boolean2(), "Publish only configured static assets; leave backend Functions, schedules and migrations unchanged"),
32541
+ backend: optional(Boolean2(), "Publish the application backend independently; do not build or activate frontend assets"),
32542
+ status: optional(String2({ format: "uuid" }), "Read a backend deployment status without publishing"),
32543
+ format: optional(stringEnum2(["text", "json"]), "Output format (default: text)")
32544
+ };
32545
+ function record6(candidate) {
32324
32546
  return candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
32325
32547
  }
32326
32548
  function requiredString(candidate, field) {
@@ -32330,7 +32552,7 @@ function requiredString(candidate, field) {
32330
32552
  return candidate.trim();
32331
32553
  }
32332
32554
  function frontendDeployment(candidate) {
32333
- const value = record5(candidate);
32555
+ const value = record6(candidate);
32334
32556
  if (!value)
32335
32557
  throw new Error("Invalid frontend deployment response");
32336
32558
  return {
@@ -32344,7 +32566,7 @@ function frontendDeployment(candidate) {
32344
32566
  }
32345
32567
  async function readJson(path) {
32346
32568
  try {
32347
- return JSON.parse(await readFile6(path, "utf8"));
32569
+ return JSON.parse(await readFile7(path, "utf8"));
32348
32570
  } catch (error) {
32349
32571
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
32350
32572
  return null;
@@ -32352,15 +32574,15 @@ async function readJson(path) {
32352
32574
  }
32353
32575
  }
32354
32576
  async function findDeployConfigRoot(startDirectory, environmentName) {
32355
- let directory = resolve24(startDirectory);
32577
+ let directory = resolve26(startDirectory);
32356
32578
  while (true) {
32357
32579
  const environmentFile = environmentName ? `.env.supacloud.${environmentName}` : null;
32358
- 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))) {
32359
32581
  return directory;
32360
32582
  }
32361
- const parent = dirname13(directory);
32583
+ const parent = dirname14(directory);
32362
32584
  if (parent === directory)
32363
- return resolve24(startDirectory);
32585
+ return resolve26(startDirectory);
32364
32586
  directory = parent;
32365
32587
  }
32366
32588
  }
@@ -32400,7 +32622,7 @@ function optionalStringArray(value, field) {
32400
32622
  });
32401
32623
  }
32402
32624
  function deployTargetConfig(candidate, name) {
32403
- const value = record5(candidate);
32625
+ const value = record6(candidate);
32404
32626
  if (!value || value.type !== "frontend" && value.type !== "edge_function") {
32405
32627
  throw new Error(`supacloud.json targets.${name}.type must be 'frontend' or 'edge_function'`);
32406
32628
  }
@@ -32425,14 +32647,14 @@ function deployTargetConfig(candidate, name) {
32425
32647
  };
32426
32648
  }
32427
32649
  async function readDeployConfig(configRoot) {
32428
- const configPath = join18(configRoot, "supacloud.json");
32650
+ const configPath = join19(configRoot, "supacloud.json");
32429
32651
  const candidate = await readJson(configPath);
32430
32652
  if (candidate === null)
32431
32653
  return {};
32432
- const config = record5(candidate);
32433
- const frontend = record5(config?.frontend);
32434
- const application = record5(config?.application);
32435
- 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);
32436
32658
  if (!config || config.frontend !== undefined && !frontend || config.application !== undefined && !application || config.targets !== undefined && !targetValues) {
32437
32659
  throw new Error("supacloud.json must contain object-valued 'application', 'frontend' or 'targets' configuration");
32438
32660
  }
@@ -32472,16 +32694,16 @@ async function readDeployConfig(configRoot) {
32472
32694
  };
32473
32695
  }
32474
32696
  async function packageName(projectDirectory) {
32475
- const candidate = record5(await readJson(join18(projectDirectory, "package.json")));
32697
+ const candidate = record6(await readJson(join19(projectDirectory, "package.json")));
32476
32698
  return typeof candidate?.name === "string" && candidate.name.trim() ? candidate.name.trim() : null;
32477
32699
  }
32478
32700
  async function pathExists(path) {
32479
32701
  return access(path).then(() => true, () => false);
32480
32702
  }
32481
32703
  async function hasProjectReleaseManifest(root) {
32482
- if (await pathExists(join18(root, "deploy-manifest.json")))
32704
+ if (await pathExists(join19(root, "deploy-manifest.json")))
32483
32705
  return true;
32484
- const directory = await opendir(join18(root, "supacloud")).catch((error) => {
32706
+ const directory = await opendir(join19(root, "supacloud")).catch((error) => {
32485
32707
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
32486
32708
  return null;
32487
32709
  throw error;
@@ -32492,28 +32714,28 @@ async function hasProjectReleaseManifest(root) {
32492
32714
  for await (const entry of directory) {
32493
32715
  if (++entries > 128)
32494
32716
  throw new Error("Too many SupaCloud project roots to resolve a deployment");
32495
- 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")))
32496
32718
  return true;
32497
32719
  }
32498
32720
  return false;
32499
32721
  }
32500
32722
  async function defaultBuildCommand(projectDirectory, configRoot) {
32501
- const packageJson = record5(await readJson(join18(projectDirectory, "package.json")));
32502
- const scripts = record5(packageJson?.scripts);
32723
+ const packageJson = record6(await readJson(join19(projectDirectory, "package.json")));
32724
+ const scripts = record6(packageJson?.scripts);
32503
32725
  if (typeof scripts?.build !== "string" || !scripts.build.trim()) {
32504
32726
  throw new Error("No build command was configured and package.json has no build script");
32505
32727
  }
32506
32728
  let directory = projectDirectory;
32507
32729
  while (true) {
32508
- 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")))
32509
32731
  return "bun run build";
32510
- if (await pathExists(join18(directory, "pnpm-lock.yaml")))
32732
+ if (await pathExists(join19(directory, "pnpm-lock.yaml")))
32511
32733
  return "pnpm run build";
32512
- if (await pathExists(join18(directory, "yarn.lock")))
32734
+ if (await pathExists(join19(directory, "yarn.lock")))
32513
32735
  return "yarn build";
32514
32736
  if (directory === configRoot)
32515
32737
  break;
32516
- const parent = dirname13(directory);
32738
+ const parent = dirname14(directory);
32517
32739
  if (parent === directory || !directory.startsWith(`${configRoot}${sep5}`))
32518
32740
  break;
32519
32741
  directory = parent;
@@ -32521,7 +32743,7 @@ async function defaultBuildCommand(projectDirectory, configRoot) {
32521
32743
  return "npm run build";
32522
32744
  }
32523
32745
  function deploymentList(candidate) {
32524
- 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;
32525
32747
  if (!values)
32526
32748
  throw new Error("Invalid frontend deployment list response");
32527
32749
  return values.map(frontendDeployment);
@@ -32581,9 +32803,9 @@ function selectFrontendTarget(configRoot, config) {
32581
32803
  throw new Error(`Multiple frontend deploy targets found (${targets.map(([name]) => name).sort().join(", ")}). Set defaultTarget to the Web target`);
32582
32804
  }
32583
32805
  function targetRoot(configRoot, target, name) {
32584
- const root = resolve24(configRoot, target.root || (name === "frontend" ? "." : name));
32585
- const prefix = `${resolve24(configRoot)}${sep5}`;
32586
- 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)) {
32587
32809
  throw new Error(`Deploy target '${name}' root must stay inside the repository root`);
32588
32810
  }
32589
32811
  return root;
@@ -32601,8 +32823,8 @@ function defaultOutputDirectory(framework) {
32601
32823
  }
32602
32824
  }
32603
32825
  function resolveInsideProject(projectDirectory, candidate, allowRoot = false) {
32604
- const resolved = resolve24(projectDirectory, candidate);
32605
- const projectRoot = resolve24(projectDirectory);
32826
+ const resolved = resolve26(projectDirectory, candidate);
32827
+ const projectRoot = resolve26(projectDirectory);
32606
32828
  const projectPrefix = `${projectRoot}${sep5}`;
32607
32829
  if (!allowRoot && resolved === projectRoot || resolved !== projectRoot && !resolved.startsWith(projectPrefix)) {
32608
32830
  throw new Error("Frontend output directory must stay inside the project directory");
@@ -32656,7 +32878,7 @@ async function collectArchiveFiles(root) {
32656
32878
  entries.push(entry);
32657
32879
  entries.sort((left, right) => left.name.localeCompare(right.name, "en"));
32658
32880
  for (const entry of entries) {
32659
- const path = join18(directory, entry.name);
32881
+ const path = join19(directory, entry.name);
32660
32882
  const stats = await lstat5(path);
32661
32883
  if (stats.isSymbolicLink())
32662
32884
  throw new Error(`Frontend output cannot contain symbolic links: ${path}`);
@@ -32675,7 +32897,7 @@ async function collectArchiveFiles(root) {
32675
32897
  const files = new Map;
32676
32898
  const digests = [];
32677
32899
  for (const path of paths) {
32678
- const data = new Uint8Array(await readFile6(path));
32900
+ const data = new Uint8Array(await readFile7(path));
32679
32901
  bytes += data.byteLength;
32680
32902
  if (bytes > MAX_SOURCE_BYTES)
32681
32903
  throw new Error(`Frontend output exceeds ${MAX_SOURCE_BYTES} bytes`);
@@ -32694,11 +32916,11 @@ async function collectArchiveFiles(root) {
32694
32916
  return { files, bytes, count: paths.length, treeSha256: tree.digest("hex") };
32695
32917
  }
32696
32918
  async function createCollectedFrontendArchive(outputDirectory, collected) {
32697
- const temporaryDirectory = await mkdtemp2(join18(tmpdir3(), "supacloud-deploy-"));
32698
- 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`);
32699
32921
  try {
32700
32922
  const archiveBytes = await createFrontendTarZstd(collected.files);
32701
- await writeFile6(archivePath, archiveBytes);
32923
+ await writeFile7(archivePath, archiveBytes);
32702
32924
  const sha256 = createHash11("sha256").update(archiveBytes).digest("hex");
32703
32925
  return {
32704
32926
  archivePath,
@@ -32743,7 +32965,13 @@ async function publishConfiguredFrontend(http, projectRef, configRoot, skipBuild
32743
32965
  }
32744
32966
  function registerDeployTools(server, http, options = {}) {
32745
32967
  registerTool(server, "deploy", "Build and publish the linked project with one command", deployToolSchema, async (args) => {
32746
- 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
+ }
32747
32975
  const projectRef = String(args.ref || options.projectRef || "").trim();
32748
32976
  if (!projectRef)
32749
32977
  throw new Error("A linked project ref is required. Set SUPACLOUD_PROJECT_REF or pass --ref");
@@ -32774,7 +33002,7 @@ function registerDeployTools(server, http, options = {}) {
32774
33002
  throw new Error("Static asset deployment requires SUPACLOUD_ENV");
32775
33003
  }
32776
33004
  await requireProjectDeploymentProtocol(http, projectRef, environmentId);
32777
- 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);
32778
33006
  if (!result)
32779
33007
  throw new Error("Static asset deployment did not produce a receipt");
32780
33008
  return result;
@@ -32809,7 +33037,7 @@ function registerDeployTools(server, http, options = {}) {
32809
33037
  ...configurationId ? { configuration_id: configurationId } : {},
32810
33038
  ...application.manifestPath ? { manifest_path: application.manifestPath } : {},
32811
33039
  ...args.skip_build === true ? { skip_build: true } : {},
32812
- ...args.json === true ? { format: "json", json: true } : {}
33040
+ ...args.format === "json" ? { format: "json" } : {}
32813
33041
  });
32814
33042
  return result;
32815
33043
  }
@@ -32818,11 +33046,11 @@ function registerDeployTools(server, http, options = {}) {
32818
33046
  }
32819
33047
 
32820
33048
  // src/shared/tools/remote-dev-tools.ts
32821
- import { existsSync as existsSync15 } from "node:fs";
32822
- import { readFile as readFile7 } from "node:fs/promises";
32823
- 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";
32824
33052
  import { createHash as createHash12 } from "node:crypto";
32825
- import { join as join19, resolve as resolve25 } from "node:path";
33053
+ import { join as join20, resolve as resolve27 } from "node:path";
32826
33054
  import { compileProject as compileProject2 } from "@supacloud/compiler";
32827
33055
  var SAFE_TOKEN = /^[A-Za-z0-9._:@/+,-]+$/;
32828
33056
  var SAFE_REMOTE_ROOT = /^\/(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+\/?$/;
@@ -32839,7 +33067,6 @@ var remoteDevToolSchema = {
32839
33067
  delete: optional(Boolean2(), "Delete remote files absent locally"),
32840
33068
  reload: optional(Boolean2(), "Reload the affected target after sync (default: true)"),
32841
33069
  interval_ms: optional(Number2(), "Watch debounce interval in milliseconds (default: 300)"),
32842
- json: optional(Boolean2(), "Emit machine-readable JSON"),
32843
33070
  apply: optional(Boolean2(), "Apply generated migrations to the selected test database"),
32844
33071
  drizzle_config: optional(String2(), "Drizzle config path"),
32845
33072
  migrations_dir: optional(String2(), "Migration directory"),
@@ -32857,8 +33084,8 @@ async function runProcess(executable, args, cwd) {
32857
33084
  function resolveDrizzleCommand(root, configured) {
32858
33085
  if (configured?.trim())
32859
33086
  return configured.trim();
32860
- const local = join19(root, "node_modules", ".bin", "drizzle-kit");
32861
- return existsSync15(local) ? local : "drizzle-kit";
33087
+ const local = join20(root, "node_modules", ".bin", "drizzle-kit");
33088
+ return existsSync17(local) ? local : "drizzle-kit";
32862
33089
  }
32863
33090
  function toolFailed(value) {
32864
33091
  if (!value || typeof value !== "object")
@@ -32868,14 +33095,14 @@ function toolFailed(value) {
32868
33095
  return result.isError === true || content.some((chunk) => chunk !== null && typeof chunk === "object" && ("text" in chunk) && typeof chunk.text === "string" && chunk.text.trimStart().startsWith("❌"));
32869
33096
  }
32870
33097
  async function migrateDatabase(args, options) {
32871
- const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
33098
+ const root = resolve27(String(args.project_dir || options.cwd || process.cwd()));
32872
33099
  const projectConfig = await readProjectConfig(root);
32873
33100
  const config = projectConfig.dev || {};
32874
33101
  const database = config.database || {};
32875
33102
  const execute = options.execute || ((command, commandArgs, cwd) => runProcess(command, commandArgs, cwd));
32876
- 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"));
32877
33104
  const migrationsDir = String(args.migrations_dir || database.migrationsDir || "supabase/migrations");
32878
- if (!existsSync15(drizzleConfig))
33105
+ if (!existsSync17(drizzleConfig))
32879
33106
  throw new Error(`Drizzle config not found: ${drizzleConfig}`);
32880
33107
  const generated = await execute(resolveDrizzleCommand(root, typeof args.drizzle_bin === "string" ? args.drizzle_bin : database.drizzleBin), ["generate", "--config", drizzleConfig], root);
32881
33108
  if (generated.exitCode !== 0)
@@ -32893,11 +33120,11 @@ async function migrateDatabase(args, options) {
32893
33120
  return { ok: true, mode: "dev", action: "migrate", generated: true, applied: true, migrations_dir: migrationsDir, result: applied };
32894
33121
  }
32895
33122
  async function readProjectConfig(root) {
32896
- const file = join19(root, "supacloud.json");
32897
- if (!existsSync15(file))
33123
+ const file = join20(root, "supacloud.json");
33124
+ if (!existsSync17(file))
32898
33125
  return {};
32899
33126
  try {
32900
- const parsed = JSON.parse(await readFile7(file, "utf8"));
33127
+ const parsed = JSON.parse(await readFile8(file, "utf8"));
32901
33128
  return parsed && typeof parsed === "object" ? parsed : {};
32902
33129
  } catch (error) {
32903
33130
  throw new Error(`Invalid supacloud.json: ${error instanceof Error ? error.message : String(error)}`);
@@ -32910,9 +33137,9 @@ async function readDevConfig(root) {
32910
33137
  async function sourceFingerprint(root) {
32911
33138
  const files = [];
32912
33139
  const visit = async (directory) => {
32913
- const entries = await readdir7(directory, { withFileTypes: true });
33140
+ const entries = await readdir8(directory, { withFileTypes: true });
32914
33141
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
32915
- const path = join19(directory, entry.name);
33142
+ const path = join20(directory, entry.name);
32916
33143
  if (entry.isDirectory() && ![".git", "node_modules", "dist", "generated", ".supacloud"].includes(entry.name))
32917
33144
  await visit(path);
32918
33145
  else if (entry.isFile())
@@ -32923,7 +33150,7 @@ async function sourceFingerprint(root) {
32923
33150
  const hash = createHash12("sha256");
32924
33151
  for (const file of files) {
32925
33152
  hash.update(file.slice(root.length).replace(/\\/g, "/"));
32926
- hash.update(await readFile7(file));
33153
+ hash.update(await readFile8(file));
32927
33154
  }
32928
33155
  return hash.digest("hex");
32929
33156
  }
@@ -32940,19 +33167,19 @@ function remoteRoot(value) {
32940
33167
  }
32941
33168
  function targetDirectory(root, target, functionSlug, config = {}) {
32942
33169
  if (target === "db")
32943
- return join19(root, "supabase", "migrations");
33170
+ return join20(root, "supabase", "migrations");
32944
33171
  if (target === "functions") {
32945
33172
  const targets = config["targets"];
32946
33173
  const entries = targets && typeof targets === "object" ? Object.values(targets) : [];
32947
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));
32948
- const base = match?.root ? resolve25(root, match.root) : join19(root, "supabase", "functions");
32949
- 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;
32950
33177
  }
32951
33178
  if (target === "frontend") {
32952
33179
  const targets = config["targets"];
32953
33180
  const entries = targets && typeof targets === "object" ? Object.values(targets) : [];
32954
33181
  const match = entries.find((entry) => entry !== null && typeof entry === "object" && ("type" in entry) && entry.type === "frontend" && (!("root" in entry) || typeof entry.root === "string"));
32955
- return match?.root ? resolve25(root, match.root) : join19(root, "apps", "web");
33182
+ return match?.root ? resolve27(root, match.root) : join20(root, "apps", "web");
32956
33183
  }
32957
33184
  return root;
32958
33185
  }
@@ -32991,10 +33218,10 @@ function reloadCommand(config, target, projectRef, functionSlug) {
32991
33218
  return [...command.split(" "), "--project-ref", safeToken(projectRef || "test", "project_ref"), "--target", targetConfigPath(target), ...functionSlug ? ["--function", safeToken(functionSlug, "function")] : []];
32992
33219
  }
32993
33220
  function sshArgs(connection, remoteCommand) {
32994
- 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];
32995
33222
  }
32996
33223
  function rsyncArgs(connection, source, destination, excludes, deleteRemote) {
32997
- 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`];
32998
33225
  if (deleteRemote)
32999
33226
  args.push("--delete-delay");
33000
33227
  for (const exclude of excludes) {
@@ -33006,17 +33233,17 @@ function rsyncArgs(connection, source, destination, excludes, deleteRemote) {
33006
33233
  return args;
33007
33234
  }
33008
33235
  async function syncOnce(args, options) {
33009
- const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
33236
+ const root = resolve27(String(args.project_dir || options.cwd || process.cwd()));
33010
33237
  const projectConfig = await readProjectConfig(root);
33011
33238
  const config = projectConfig.dev || {};
33012
33239
  const target = String(args.target || "project");
33013
33240
  const source = targetDirectory(root, target, typeof args.function === "string" ? args.function : undefined, projectConfig);
33014
- if (!existsSync15(source))
33241
+ if (!existsSync17(source))
33015
33242
  throw new Error(`Dev source directory not found: ${source}`);
33016
33243
  let compiled = false;
33017
33244
  if (config.compile === true && target !== "db") {
33018
- const compileRoot = resolve25(root, config.compileRoot || ".");
33019
- const compileOutDir = resolve25(root, config.compileOutDir || "generated");
33245
+ const compileRoot = resolve27(root, config.compileRoot || ".");
33246
+ const compileOutDir = resolve27(root, config.compileOutDir || "generated");
33020
33247
  const compilation = await compileProject2({
33021
33248
  rootDir: compileRoot,
33022
33249
  outDir: compileOutDir,
@@ -33054,7 +33281,7 @@ function registerRemoteDevTools(server, options = {}) {
33054
33281
  if (["production", "prod"].includes((options.environment || "").toLowerCase()))
33055
33282
  throw new Error("Remote dev mode is forbidden for production environments");
33056
33283
  if (args.action === "status") {
33057
- const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
33284
+ const root = resolve27(String(args.project_dir || options.cwd || process.cwd()));
33058
33285
  const config = await readDevConfig(root);
33059
33286
  const connection = connectionArgs(options, config, args);
33060
33287
  const execute = options.execute || ((command, commandArgs, cwd) => runProcess(command, commandArgs, cwd));
@@ -33068,7 +33295,7 @@ function registerRemoteDevTools(server, options = {}) {
33068
33295
  return { content: [{ type: "text", text: JSON.stringify(await migrateDatabase(args, options), null, 2) }] };
33069
33296
  }
33070
33297
  if (args.action === "watch") {
33071
- const root = resolve25(String(args.project_dir || options.cwd || process.cwd()));
33298
+ const root = resolve27(String(args.project_dir || options.cwd || process.cwd()));
33072
33299
  const interval = Math.max(100, Math.min(1e4, Number(args.interval_ms || 300)));
33073
33300
  let fingerprint = "";
33074
33301
  let lastSync = null;
@@ -33088,215 +33315,19 @@ function registerRemoteDevTools(server, options = {}) {
33088
33315
  }
33089
33316
 
33090
33317
  // src/shared/tools/developer-experience-tools.ts
33091
- import { existsSync as existsSync17 } from "node:fs";
33092
- import { mkdir as mkdir7, readdir as readdir8, readFile as readFile8, writeFile as writeFile7 } from "node:fs/promises";
33093
- import { dirname as dirname14, join as join20, resolve as resolve27 } from "node:path";
33094
-
33095
- // src/shared/context.ts
33096
- import { existsSync as existsSync16, readFileSync as readFileSync12 } from "node:fs";
33097
- import { homedir as homedir3 } from "node:os";
33098
- import { resolve as resolve26 } from "node:path";
33099
- var CORE_CONTEXT_KEYS2 = [
33100
- "SUPABASE_URL",
33101
- "SUPABASE_SERVICE_ROLE_KEY",
33102
- "SUPACLOUD_API_URL",
33103
- "SUPACLOUD_MANAGEMENT_API_URL",
33104
- "MANAGEMENT_API_URL",
33105
- "SUPACLOUD_API_TOKEN",
33106
- "SUPACLOUD_PROJECT_REF",
33107
- "X_PROJECT_REF",
33108
- "SUPACLOUD_HOST",
33109
- "SUPACLOUD_APPLICATION_ID",
33110
- "SUPACLOUD_APPLICATION_ENVIRONMENT_ID",
33111
- "SUPACLOUD_APPLICATION_CONFIGURATION_PATH",
33112
- "SUPACLOUD_APPLICATION_CONFIGURATION_ID",
33113
- "SUPACLOUD_APPLICATION_MANIFEST_PATH"
33114
- ];
33115
- function unquotedEnvValue2(rawValue) {
33116
- const value = rawValue.trim();
33117
- const quote = value[0];
33118
- return quote && (quote === '"' || quote === "'") && value.endsWith(quote) ? value.slice(1, -1) : value;
33119
- }
33120
- function parseEnvFile2(contents) {
33121
- const values = {};
33122
- for (const rawLine of contents.split(/\r?\n/)) {
33123
- const line = rawLine.trim();
33124
- if (!line || line.startsWith("#"))
33125
- continue;
33126
- const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
33127
- if (!match)
33128
- continue;
33129
- values[match[1]] = unquotedEnvValue2(match[2]);
33130
- }
33131
- return values;
33132
- }
33133
- function readEnvFile2(path, required) {
33134
- if (!existsSync16(path)) {
33135
- if (required)
33136
- throw new Error(`SupaCloud environment file not found: ${path}`);
33137
- return {};
33138
- }
33139
- try {
33140
- return parseEnvFile2(readFileSync12(path, "utf8"));
33141
- } catch (error) {
33142
- const message = error instanceof Error ? error.message : String(error);
33143
- throw new Error(`Failed to read SupaCloud environment file ${path}: ${message}`);
33144
- }
33145
- }
33146
- function canonicalApiOrigin2(value) {
33147
- const candidate = value.trim();
33148
- if (!candidate)
33149
- return "";
33150
- try {
33151
- const url = new URL(candidate);
33152
- const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
33153
- const allowedProtocol = url.protocol === "https:" || url.protocol === "http:" && loopback;
33154
- const exactOrigin = candidate === url.origin || candidate === `${url.origin}/`;
33155
- return allowedProtocol && !url.username && !url.password && exactOrigin && url.pathname === "/" && !url.search && !url.hash ? url.origin : "";
33156
- } catch (error) {
33157
- if (error instanceof TypeError)
33158
- return "";
33159
- throw error;
33160
- }
33161
- }
33162
- function hostFromUrl2(value) {
33163
- return value ? new URL(value).hostname : "";
33164
- }
33165
- function inferProjectRefFromSupabaseUrl2(value) {
33166
- if (!value)
33167
- return "";
33168
- return new URL(value).hostname.match(/^([a-z0-9-]+)\.api\./i)?.[1] ?? "";
33169
- }
33170
- function sourceCredentialScope2(values, explicitApiUrl, supabaseUrl) {
33171
- const hasManagementContext = Boolean(explicitApiUrl.trim() || values.SUPACLOUD_API_TOKEN?.trim() || values.SUPACLOUD_HOST?.trim());
33172
- if (hasManagementContext)
33173
- return "management";
33174
- return supabaseUrl || values.SUPABASE_SERVICE_ROLE_KEY?.trim() ? "project_application" : "incomplete";
33175
- }
33176
- function sourceProjectCore2(values) {
33177
- const supabaseUrl = canonicalApiOrigin2(values.SUPABASE_URL || "");
33178
- const projectRef = (values.SUPACLOUD_PROJECT_REF || values.X_PROJECT_REF || "").trim() || inferProjectRefFromSupabaseUrl2(supabaseUrl);
33179
- const explicitApiUrl = values.SUPACLOUD_API_URL || values.SUPACLOUD_MANAGEMENT_API_URL || values.MANAGEMENT_API_URL || "";
33180
- const credentialScope = sourceCredentialScope2(values, explicitApiUrl, supabaseUrl);
33181
- const managementUrl = explicitApiUrl || (values.SUPACLOUD_HOST ? `http://${values.SUPACLOUD_HOST}:9090` : "");
33182
- const apiUrl = credentialScope === "management" ? canonicalApiOrigin2(managementUrl) : "";
33183
- const apiToken = credentialScope === "management" ? values.SUPACLOUD_API_TOKEN || "" : "";
33184
- return { apiUrl, apiToken, projectRef, supabaseUrl, credentialScope };
33185
- }
33186
- function processValues2(env) {
33187
- return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== undefined));
33188
- }
33189
- function hasProcessContext2(env) {
33190
- return CORE_CONTEXT_KEYS2.some((key) => Object.hasOwn(env, key));
33191
- }
33192
- function completeProjectContext2(values) {
33193
- const core = sourceProjectCore2(values);
33194
- if (core.credentialScope === "project_application") {
33195
- return Boolean(core.supabaseUrl && values.SUPABASE_SERVICE_ROLE_KEY?.trim() && core.projectRef);
33196
- }
33197
- return Boolean(core.apiUrl && core.apiToken && core.projectRef);
33198
- }
33199
- function parseInsecureTls2(values) {
33200
- for (const key of ["SUPACLOUD_TLS_VERIFY", "SUPACLOUD_TLS_INSECURE"]) {
33201
- const value = values[key]?.trim().toLowerCase();
33202
- if (value === undefined)
33203
- continue;
33204
- if (["1", "true", "yes", "on"].includes(value))
33205
- return key === "SUPACLOUD_TLS_INSECURE";
33206
- if (["0", "false", "no", "off"].includes(value))
33207
- return key === "SUPACLOUD_TLS_VERIFY";
33208
- throw new Error(`${key} must be a boolean (true or false)`);
33209
- }
33210
- return true;
33211
- }
33212
- function namedEnvironmentSource2(cwd, selector) {
33213
- const environment = normalizeEnvironmentName(selector);
33214
- const path = resolve26(cwd, `.env.supacloud.${selector}`);
33215
- const values = readEnvFile2(path, true);
33216
- if (values.SUPACLOUD_ENV && normalizeEnvironmentName(values.SUPACLOUD_ENV) !== environment) {
33217
- throw new Error(`SUPACLOUD_ENV in ${path} does not match selector ${selector}`);
33218
- }
33219
- return { values, kind: "named_env_file", path, environment };
33220
- }
33221
- function explicitEnvironmentSource2(cwd, envFile) {
33222
- const path = resolve26(cwd, envFile);
33223
- const values = readEnvFile2(path, true);
33224
- if (!values.SUPACLOUD_ENV)
33225
- throw new Error(`SUPACLOUD_ENV is required in ${path}`);
33226
- return {
33227
- values,
33228
- kind: "explicit_env_file",
33229
- path,
33230
- environment: normalizeEnvironmentName(values.SUPACLOUD_ENV)
33231
- };
33232
- }
33233
- function contextSource2(env, cwd, selection) {
33234
- if (selection.environmentName && selection.envFile)
33235
- throw new Error("Environment selectors are mutually exclusive");
33236
- if (selection.environmentName)
33237
- return namedEnvironmentSource2(cwd, selection.environmentName);
33238
- if (selection.envFile)
33239
- return explicitEnvironmentSource2(cwd, selection.envFile);
33240
- if (env.SUPACLOUD_ENV) {
33241
- const environment = normalizeEnvironmentName(env.SUPACLOUD_ENV);
33242
- const values = processValues2(env);
33243
- if (completeProjectContext2(values)) {
33244
- return { values, kind: "process_env", path: null, environment };
33245
- }
33246
- return namedEnvironmentSource2(cwd, env.SUPACLOUD_ENV);
33247
- }
33248
- if (hasProcessContext2(env)) {
33249
- return { values: processValues2(env), kind: "process_env", path: null, environment: "" };
33250
- }
33251
- const path = resolve26(cwd, ".env");
33252
- const values = readEnvFile2(path, false);
33253
- const environment = values.SUPACLOUD_ENV ? normalizeEnvironmentName(values.SUPACLOUD_ENV) : "";
33254
- return { values, kind: Object.keys(values).length ? "legacy_dotenv" : "none", path: existsSync16(path) ? path : null, environment };
33255
- }
33256
- function resolveSupaCloudContext2(env = process.env, cwd = process.cwd(), selection = {}) {
33257
- const source = contextSource2(env, cwd, selection);
33258
- const core = sourceProjectCore2(source.values);
33259
- const host = source.values.SUPACLOUD_HOST || hostFromUrl2(core.apiUrl || core.supabaseUrl);
33260
- const readOnly = env.SUPACLOUD_READ_ONLY === "true" || source.values.SUPACLOUD_READ_ONLY === "true";
33261
- const insecureTls = parseInsecureTls2(source.values);
33262
- return {
33263
- host,
33264
- sshUser: env.SUPACLOUD_SSH_USER ?? "root",
33265
- sshPort: parseInt(env.SUPACLOUD_SSH_PORT ?? "22", 10),
33266
- sshKey: env.SUPACLOUD_SSH_KEY ?? resolve26(homedir3(), ".ssh", "id_rsa"),
33267
- sshPass: env.SUPACLOUD_SSH_PASS ?? "",
33268
- apiUrl: core.apiUrl,
33269
- apiToken: core.apiToken,
33270
- projectRef: core.projectRef,
33271
- readOnly,
33272
- environment: source.environment,
33273
- production: source.environment === "prod" || source.environment === "production",
33274
- inferredSupabaseUrl: core.supabaseUrl,
33275
- inferredServiceRoleKey: source.values.SUPABASE_SERVICE_ROLE_KEY || "",
33276
- credentialScope: core.credentialScope,
33277
- source: source.kind,
33278
- sourcePath: source.path,
33279
- environmentVariables: source.values,
33280
- applicationId: source.values.SUPACLOUD_APPLICATION_ID?.trim() || "",
33281
- applicationEnvironmentId: source.values.SUPACLOUD_APPLICATION_ENVIRONMENT_ID?.trim() || source.environment,
33282
- applicationConfigurationPath: source.values.SUPACLOUD_APPLICATION_CONFIGURATION_PATH?.trim() || "",
33283
- applicationConfigurationId: source.values.SUPACLOUD_APPLICATION_CONFIGURATION_ID?.trim() || "",
33284
- applicationManifestPath: source.values.SUPACLOUD_APPLICATION_MANIFEST_PATH?.trim() || "",
33285
- insecureTls
33286
- };
33287
- }
33288
-
33289
- // src/shared/tools/developer-experience-tools.ts
33290
- 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 = {
33291
33322
  root: optional(String2(), "Project directory to initialize (default: current directory)"),
33292
- json: optional(Boolean2(), "Emit one machine-readable JSON result")
33323
+ format: optional(stringEnum2(["text", "json"]), "Output format (default: text)")
33293
33324
  };
33294
- var environmentToolSchema = {
33325
+ var environmentToolSchema2 = {
33295
33326
  action: withDescription(stringEnum2(["list", "check"]), "Environment profile inspection action"),
33296
33327
  root: optional(String2(), "Project directory containing .env.supacloud.* files"),
33297
- json: optional(Boolean2(), "Emit one machine-readable JSON result")
33328
+ format: optional(stringEnum2(["text", "json"]), "Output format (default: text)")
33298
33329
  };
33299
- var TEST_PROFILE = [
33330
+ var TEST_PROFILE2 = [
33300
33331
  "SUPACLOUD_ENV=test",
33301
33332
  "SUPACLOUD_API_URL=https://management.test.example.com",
33302
33333
  "SUPACLOUD_API_TOKEN=<test-management-api-token>",
@@ -33305,7 +33336,7 @@ var TEST_PROFILE = [
33305
33336
  ""
33306
33337
  ].join(`
33307
33338
  `);
33308
- var PRODUCTION_PROFILE = [
33339
+ var PRODUCTION_PROFILE2 = [
33309
33340
  "SUPACLOUD_ENV=production",
33310
33341
  "SUPACLOUD_API_URL=https://management.example.com",
33311
33342
  "SUPACLOUD_API_TOKEN=<production-management-api-token>",
@@ -33314,41 +33345,41 @@ var PRODUCTION_PROFILE = [
33314
33345
  ""
33315
33346
  ].join(`
33316
33347
  `);
33317
- function textResult3(text, isError = false) {
33348
+ function textResult4(text, isError = false) {
33318
33349
  return { isError, content: [{ type: "text", text }] };
33319
33350
  }
33320
- function jsonResult(value, isError = false) {
33321
- return textResult3(JSON.stringify(value, null, 2), isError);
33351
+ function jsonResult2(value, isError = false) {
33352
+ return textResult4(JSON.stringify(value, null, 2), isError);
33322
33353
  }
33323
- function record6(value) {
33354
+ function record7(value) {
33324
33355
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
33325
33356
  }
33326
- function packageDependencies(manifest) {
33357
+ function packageDependencies2(manifest) {
33327
33358
  return new Set([
33328
33359
  ...Object.keys(manifest.dependencies ?? {}),
33329
33360
  ...Object.keys(manifest.devDependencies ?? {})
33330
33361
  ]);
33331
33362
  }
33332
- function outputDirectoryFor(manifest) {
33333
- const dependencies = packageDependencies(manifest);
33363
+ function outputDirectoryFor2(manifest) {
33364
+ const dependencies = packageDependencies2(manifest);
33334
33365
  if (dependencies.has("next"))
33335
33366
  return "out";
33336
33367
  if (dependencies.has("@sveltejs/kit") || dependencies.has("svelte"))
33337
33368
  return "build";
33338
33369
  return "dist";
33339
33370
  }
33340
- async function readPackageManifest(root) {
33341
- const path = join20(root, "package.json");
33342
- if (!existsSync17(path))
33371
+ async function readPackageManifest2(root) {
33372
+ const path = join21(root, "package.json");
33373
+ if (!existsSync18(path))
33343
33374
  return {};
33344
33375
  try {
33345
- const parsed = JSON.parse(await readFile8(path, "utf8"));
33346
- const value = record6(parsed);
33376
+ const parsed = JSON.parse(await readFile9(path, "utf8"));
33377
+ const value = record7(parsed);
33347
33378
  if (!value)
33348
33379
  throw new Error("package.json must contain an object");
33349
- const scripts = record6(value.scripts);
33350
- const dependencies = record6(value.dependencies);
33351
- const devDependencies = record6(value.devDependencies);
33380
+ const scripts = record7(value.scripts);
33381
+ const dependencies = record7(value.dependencies);
33382
+ const devDependencies = record7(value.devDependencies);
33352
33383
  return {
33353
33384
  scripts: scripts ?? undefined,
33354
33385
  dependencies: dependencies ?? undefined,
@@ -33358,9 +33389,9 @@ async function readPackageManifest(root) {
33358
33389
  throw new Error(`Invalid package.json: ${error instanceof Error ? error.message : String(error)}`);
33359
33390
  }
33360
33391
  }
33361
- async function writeNewFile(path, content) {
33392
+ async function writeNewFile2(path, content) {
33362
33393
  try {
33363
- await writeFile7(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
33394
+ await writeFile8(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
33364
33395
  return true;
33365
33396
  } catch (error) {
33366
33397
  if (error instanceof Error && "code" in error && error.code === "EEXIST")
@@ -33368,9 +33399,9 @@ async function writeNewFile(path, content) {
33368
33399
  throw error;
33369
33400
  }
33370
33401
  }
33371
- async function ensureGitignore(root) {
33372
- const path = join20(root, ".gitignore");
33373
- 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") : "";
33374
33405
  const required = [".env.supacloud.*", "!.env.supacloud.*.example"];
33375
33406
  const missing = required.filter((entry) => !existing.split(/\r?\n/).includes(entry));
33376
33407
  if (missing.length === 0)
@@ -33378,21 +33409,21 @@ async function ensureGitignore(root) {
33378
33409
  const prefix = existing.length > 0 && !existing.endsWith(`
33379
33410
  `) ? `${existing}
33380
33411
  ` : existing;
33381
- await writeFile7(path, `${prefix}${missing.join(`
33412
+ await writeFile8(path, `${prefix}${missing.join(`
33382
33413
  `)}
33383
33414
  `, "utf8");
33384
33415
  return true;
33385
33416
  }
33386
- async function initializeProject(request, options) {
33387
- const root = resolve27(request.root || options.cwd || process.cwd());
33388
- await mkdir7(root, { recursive: true });
33389
- const configPath = join20(root, "supacloud.json");
33390
- 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)) {
33391
33422
  throw new Error(`supacloud.json already exists in ${root}; init never overwrites project configuration`);
33392
33423
  }
33393
- const manifest = await readPackageManifest(root);
33424
+ const manifest = await readPackageManifest2(root);
33394
33425
  const hasBuildScript = typeof manifest.scripts?.build === "string" && Boolean(manifest.scripts.build.trim());
33395
- const outputDirectory = outputDirectoryFor(manifest);
33426
+ const outputDirectory = outputDirectoryFor2(manifest);
33396
33427
  const config = {
33397
33428
  frontend: {
33398
33429
  root: ".",
@@ -33401,7 +33432,7 @@ async function initializeProject(request, options) {
33401
33432
  };
33402
33433
  const created = [];
33403
33434
  try {
33404
- await writeFile7(configPath, `${JSON.stringify(config, null, 2)}
33435
+ await writeFile8(configPath, `${JSON.stringify(config, null, 2)}
33405
33436
  `, {
33406
33437
  encoding: "utf8",
33407
33438
  flag: "wx",
@@ -33414,13 +33445,13 @@ async function initializeProject(request, options) {
33414
33445
  throw error;
33415
33446
  }
33416
33447
  created.push("supacloud.json");
33417
- if (await writeNewFile(join20(root, ".env.supacloud.test.example"), TEST_PROFILE)) {
33448
+ if (await writeNewFile2(join21(root, ".env.supacloud.test.example"), TEST_PROFILE2)) {
33418
33449
  created.push(".env.supacloud.test.example");
33419
33450
  }
33420
- if (await writeNewFile(join20(root, ".env.supacloud.production.example"), PRODUCTION_PROFILE)) {
33451
+ if (await writeNewFile2(join21(root, ".env.supacloud.production.example"), PRODUCTION_PROFILE2)) {
33421
33452
  created.push(".env.supacloud.production.example");
33422
33453
  }
33423
- if (await ensureGitignore(root))
33454
+ if (await ensureGitignore2(root))
33424
33455
  created.push(".gitignore");
33425
33456
  const result = {
33426
33457
  schema: "supacloud.developer-experience.init.v1",
@@ -33428,7 +33459,7 @@ async function initializeProject(request, options) {
33428
33459
  root,
33429
33460
  created,
33430
33461
  detected: {
33431
- package_json: existsSync17(join20(root, "package.json")),
33462
+ package_json: existsSync18(join21(root, "package.json")),
33432
33463
  build_script: hasBuildScript,
33433
33464
  output_directory: outputDirectory
33434
33465
  },
@@ -33439,9 +33470,9 @@ async function initializeProject(request, options) {
33439
33470
  "supacloud --env test deploy"
33440
33471
  ]
33441
33472
  };
33442
- if (request.json === true)
33443
- return jsonResult(result);
33444
- return textResult3([
33473
+ if (request.format === "json")
33474
+ return jsonResult2(result);
33475
+ return textResult4([
33445
33476
  `Initialized SupaCloud deployment in ${root}.`,
33446
33477
  `Created: ${created.join(", ")}.`,
33447
33478
  hasBuildScript ? `Detected package.json build script; deploy output defaults to ${outputDirectory}.` : "No package.json build script detected; add one before deploying.",
@@ -33455,14 +33486,14 @@ function profileNameFromFile(name) {
33455
33486
  return match?.[1] ?? null;
33456
33487
  }
33457
33488
  async function environmentProfiles(root) {
33458
- const entries = await readdir8(root, { withFileTypes: true }).catch((error) => {
33489
+ const entries = await readdir9(root, { withFileTypes: true }).catch((error) => {
33459
33490
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
33460
33491
  return [];
33461
33492
  throw error;
33462
33493
  });
33463
33494
  return entries.filter((entry) => entry.isFile()).map((entry) => {
33464
33495
  const name = profileNameFromFile(entry.name);
33465
- return name ? { name, path: join20(root, entry.name), exists: true } : null;
33496
+ return name ? { name, path: join21(root, entry.name), exists: true } : null;
33466
33497
  }).filter((profile) => profile !== null).sort((left, right) => left.name.localeCompare(right.name));
33467
33498
  }
33468
33499
  function statusMissing(context) {
@@ -33497,18 +33528,18 @@ function deployMissing(context, application, hasFrontend = false) {
33497
33528
  }
33498
33529
  async function inspectProfile(profile) {
33499
33530
  try {
33500
- const context = resolveSupaCloudContext2({}, dirname14(profile.path), {
33531
+ const context = resolveSupaCloudContext2({}, dirname15(profile.path), {
33501
33532
  environmentName: profile.name
33502
33533
  });
33503
33534
  const status = statusMissing(context);
33504
- 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) => {
33505
33536
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
33506
33537
  return "{}";
33507
33538
  throw error;
33508
33539
  });
33509
- const config = record6(JSON.parse(configText));
33510
- const hasFrontend = record6(config?.frontend) !== null || Object.values(record6(config?.targets) ?? {}).some((target) => record6(target)?.type === "frontend");
33511
- 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);
33512
33543
  return {
33513
33544
  name: profile.name,
33514
33545
  path: profile.path,
@@ -33545,7 +33576,7 @@ async function inspectProfile(profile) {
33545
33576
  }
33546
33577
  }
33547
33578
  async function inspectEnvironment(request, options) {
33548
- const root = resolve27(request.root || options.cwd || process.cwd());
33579
+ const root = resolve28(request.root || options.cwd || process.cwd());
33549
33580
  const profiles = await environmentProfiles(root);
33550
33581
  if (request.action === "list") {
33551
33582
  const active = options.activeContext?.environment || null;
@@ -33560,13 +33591,13 @@ async function inspectEnvironment(request, options) {
33560
33591
  active: profile.name.toLowerCase() === active?.toLowerCase()
33561
33592
  }))
33562
33593
  };
33563
- if (request.json === true)
33564
- return jsonResult(result);
33594
+ if (request.format === "json")
33595
+ return jsonResult2(result);
33565
33596
  if (profiles.length === 0) {
33566
- return textResult3(`No .env.supacloud.<name> profiles found in ${root}.
33567
- 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.`);
33568
33599
  }
33569
- return textResult3([
33600
+ return textResult4([
33570
33601
  `Environment profiles in ${root}:`,
33571
33602
  ...result.environments.map((profile) => `${profile.active ? "*" : " "} ${profile.name} ${profile.path}`)
33572
33603
  ].join(`
@@ -33580,11 +33611,11 @@ Run \`supacloud init\` to create safe examples.`);
33580
33611
  active: options.activeContext?.environment || null,
33581
33612
  environments: reports
33582
33613
  };
33583
- if (request.json === true)
33584
- return jsonResult(result, !result.ok);
33614
+ if (request.format === "json")
33615
+ return jsonResult2(result, !result.ok);
33585
33616
  if (reports.length === 0) {
33586
- return textResult3(`No environment profiles found in ${root}.
33587
- 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);
33588
33619
  }
33589
33620
  const lines = reports.map((report) => {
33590
33621
  if (report.error)
@@ -33594,12 +33625,11 @@ Run \`supacloud init\` and copy an example profile.`, true);
33594
33625
  const ref = report.projectRef ? `project=${report.projectRef}` : "project=unknown";
33595
33626
  return `${status} ${report.name} ${deploy} ${ref}`;
33596
33627
  });
33597
- return textResult3(lines.join(`
33628
+ return textResult4(lines.join(`
33598
33629
  `), !result.ok);
33599
33630
  }
33600
33631
  function registerDeveloperExperienceTools(server, options = {}) {
33601
- registerTool(server, "init", "Create SupaCloud deployment configuration and non-secret environment examples without overwriting project files.", initToolSchema, (request) => initializeProject(request, options));
33602
- 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));
33603
33633
  }
33604
33634
 
33605
33635
  // src/shared/tools/project-deployment.ts
@@ -33727,7 +33757,7 @@ async function runProjectDeployment(http, args) {
33727
33757
  // package.json
33728
33758
  var package_default = {
33729
33759
  name: "@supacloud/cli",
33730
- version: "0.114.0",
33760
+ version: "0.115.1",
33731
33761
  description: "Project-scoped CLI for SupaCloud users",
33732
33762
  type: "module",
33733
33763
  main: "./dist/index.js",
@@ -33779,9 +33809,9 @@ var package_default = {
33779
33809
  directory: "packages/cli"
33780
33810
  },
33781
33811
  dependencies: {
33782
- "@supacloud/compiler": "^0.53.1",
33812
+ "@supacloud/compiler": "^0.54.0",
33783
33813
  "@supacloud/delivery": "0.24.0",
33784
- "@supacloud/db": "0.20.3",
33814
+ "@supacloud/db": "0.20.4",
33785
33815
  typebox: "1.4.3"
33786
33816
  },
33787
33817
  overrides: {
@@ -33791,10 +33821,10 @@ var package_default = {
33791
33821
  },
33792
33822
  devDependencies: {
33793
33823
  "@supabase/supabase-js": "2.118.0",
33794
- "@supacloud/elysia": "0.37.0",
33824
+ "@supacloud/elysia": "0.38.0",
33795
33825
  "@types/bun": "^1.4.4",
33796
33826
  effect: "4.0.5",
33797
- elysia: "2.0.0-rc.0",
33827
+ elysia: "2.0.0-rc.1",
33798
33828
  typescript: "^7.0.2"
33799
33829
  }
33800
33830
  };
@@ -33963,7 +33993,7 @@ USAGE
33963
33993
 
33964
33994
  ${preferredCommand} [global flags] deploy [--flags]
33965
33995
  ${preferredCommand} [global flags] <module> <action> [--flags]
33966
- ${preferredCommand} init [--root <directory>]
33996
+ ${preferredCommand} deploy init [--root <directory>]
33967
33997
  ${preferredCommand} env <list|check> [--root <directory>]
33968
33998
  ${preferredCommand} [global flags] status
33969
33999
  ${preferredCommand} --help
@@ -33997,9 +34027,9 @@ DEFAULT CONTEXT
33997
34027
 
33998
34028
  EXAMPLES
33999
34029
 
34000
- ${preferredCommand} init
34030
+ ${preferredCommand} deploy init
34001
34031
  ${preferredCommand} env check
34002
- ${preferredCommand} env check --json
34032
+ ${preferredCommand} env check --format json
34003
34033
  ${preferredCommand} status
34004
34034
  ${preferredCommand} deploy
34005
34035
  ${preferredCommand} --env test deploy
@@ -34034,7 +34064,7 @@ EXAMPLES
34034
34064
  ${preferredCommand} supabase db_dump --db_url "postgresql://..." --file backups/schema.sql
34035
34065
  ${preferredCommand} lite migrate --project_dir .
34036
34066
  ${preferredCommand} lite start --project_dir . --port 54321
34037
- ${preferredCommand} lite doctor --project_dir . --json
34067
+ ${preferredCommand} lite doctor --project_dir . --format json
34038
34068
  ${preferredCommand} branch create --name feature-auth --data_mode schema_only
34039
34069
  ${preferredCommand} branch promotion_plan --branch_ref preview123
34040
34070
  ${preferredCommand} branch promote --branch_ref preview123 --plan_checksum <sha256>
@@ -34042,8 +34072,6 @@ EXAMPLES
34042
34072
  ${preferredCommand} ai install_skill --dry_run
34043
34073
  ${preferredCommand} mcp config
34044
34074
  ${preferredCommand} app init --name my-service
34045
- ${preferredCommand} app init --name my-api --template http
34046
- ${preferredCommand} app init --name my-worker --template edge
34047
34075
  ${preferredCommand} app generate --kind module --name billing
34048
34076
  ${preferredCommand} app generate --kind command --module billing --name issue-invoice
34049
34077
  ${preferredCommand} app generate --kind job --module billing --name sync-orders
@@ -34084,13 +34112,6 @@ EXAMPLES
34084
34112
  ${preferredCommand} app context --root . --target CaseModule --events execution.ndjson --request-id request-123 --format json
34085
34113
  ${preferredCommand} app doctor --root .
34086
34114
  ${preferredCommand} app fix --root . --fix fix.json --write
34087
- ${preferredCommand} generate --kind module --name billing
34088
- ${preferredCommand} generate --kind job --module billing --name sync-orders
34089
- ${preferredCommand} generate --kind contract --module billing --name issue-invoice
34090
- ${preferredCommand} check --root . --strict
34091
- ${preferredCommand} context --format json
34092
- ${preferredCommand} context --target CaseModule --format json
34093
- ${preferredCommand} doctor
34094
34115
  ${preferredCommand} db context --root . --module_file db/modules.ts --module cases
34095
34116
  ${preferredCommand} db context --root . --module_file db/modules.ts --target public.case_create
34096
34117
  ${preferredCommand} db lint --root . --module_file db/modules.ts
@@ -34102,6 +34123,7 @@ EXAMPLES
34102
34123
  ${preferredCommand} db plan --dir supabase/migrations
34103
34124
  ${preferredCommand} db apply --dir supabase/migrations --approved_digest <reviewed-digest>
34104
34125
  ${preferredCommand} db role_check --schema public
34126
+ ${preferredCommand} db role_check --remote --schema public
34105
34127
  ${preferredCommand} db role_sql --application_role app_user --migration_role app_migrator --database app_db
34106
34128
  ${preferredCommand} edge_functions get_config --ref abc123 --slug hello
34107
34129
  ${preferredCommand} edge_functions deploy --ref abc123 --slug hello --path ./supabase/functions/hello --expected-active-version absent --expected-activation-id legacy
@@ -34164,7 +34186,6 @@ function createCliTools(context, confirmProduction) {
34164
34186
  activeContext: context
34165
34187
  })));
34166
34188
  Object.assign(tools, captureTools((server) => registerAppTools(server, { onDevProgress: writeDevProgress })));
34167
- Object.assign(tools, captureTools((server) => registerAppAliases(server)));
34168
34189
  Object.assign(tools, captureTools((server) => registerDbGovernanceTools(server, {
34169
34190
  projectRef: context.projectRef || undefined,
34170
34191
  apiUrl: context.apiUrl || undefined,
@@ -34199,7 +34220,7 @@ function createCliTools(context, confirmProduction) {
34199
34220
  ]
34200
34221
  })
34201
34222
  };
34202
- 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"]) {
34203
34224
  tools[name] = {
34204
34225
  schema: { action: genericActionSchema },
34205
34226
  callback: async () => ({
@@ -34226,7 +34247,6 @@ function createCliTools(context, confirmProduction) {
34226
34247
  tools.branch = { schema: branchHelpTool.schema, callback: branchContextCallback };
34227
34248
  }
34228
34249
  const frontendContextCallback = tools.frontend.callback;
34229
- tools.applications = { schema: APPLICATION_TOOL_SCHEMA, callback: tools.applications.callback };
34230
34250
  const frontendHelpTool = captureTools((server) => registerFrontendTools(server, {})).frontend;
34231
34251
  if (frontendHelpTool) {
34232
34252
  tools.frontend = { schema: frontendHelpTool.schema, callback: frontendContextCallback };
@@ -34236,13 +34256,22 @@ function createCliTools(context, confirmProduction) {
34236
34256
  registerContextAwareHelp();
34237
34257
  tools.deploy = {
34238
34258
  schema: deployToolSchema,
34239
- callback: async () => ({
34240
- isError: true,
34241
- content: [{
34242
- type: "text",
34243
- text: `\u26A0\uFE0F Deploy requires Management API context. Run \`${preferredCommand} status\` to inspect current detection.`
34244
- }]
34245
- })
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
+ }
34246
34275
  };
34247
34276
  Object.assign(tools, captureTools((server) => registerDatabaseTools(server, undefined, {
34248
34277
  localOnly: true
@@ -34341,9 +34370,9 @@ function createCliTools(context, confirmProduction) {
34341
34370
  applicationOrigin: context.inferredSupabaseUrl || undefined
34342
34371
  })));
34343
34372
  assign(captureTools((server) => registerFrontendTools(server, http)));
34344
- assign(captureTools((server) => registerApplicationTools(server, http)));
34373
+ const applicationTools = captureTools((server) => registerApplicationTools(server, http));
34345
34374
  Object.assign(tools, captureTools((server) => registerAppTools(server, {
34346
- getApplications: () => tools.applications?.callback,
34375
+ getApplications: () => applicationTools.applications?.callback,
34347
34376
  onDevProgress: writeDevProgress,
34348
34377
  projectRef: context.projectRef || undefined,
34349
34378
  projectDeployment: (args) => runProjectDeployment(http, args)
@@ -34398,7 +34427,7 @@ async function main() {
34398
34427
  }
34399
34428
  const cliTools = createCliTools(context, globalOptions.confirmProduction);
34400
34429
  if (args.length === 1 && !["ai", "supabase", "lite", "app", "db", "env"].includes(args[0]) && cliTools[args[0]]) {
34401
- if ("action" in cliTools[args[0]].schema) {
34430
+ if ("action" in cliTools[args[0]].schema && args[0] !== "deploy") {
34402
34431
  if (context.credentialScope !== "management" || !context.apiUrl || !context.apiToken) {
34403
34432
  console.error(`This command requires Management API context. Run \`${preferredCommand} status\` to inspect current detection.`);
34404
34433
  process.exitCode = 1;