@supacloud/cli 0.87.1 → 0.90.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7797,7 +7797,7 @@ function FromObject10(context, type, value) {
7797
7797
  return value;
7798
7798
  const additionalProperties = GetAdditionalProperties(type);
7799
7799
  for (const key of Keys(value)) {
7800
- if (HasPropertyKey(type.properties, key)) {
7800
+ if (HasPropertyKey(type.properties, key) && IsSchema(type.properties[key])) {
7801
7801
  value[key] = FromType19(context, type.properties[key], value[key]);
7802
7802
  continue;
7803
7803
  }
@@ -7882,6 +7882,9 @@ function FromProperties4(properties) {
7882
7882
  result[key] = FromType20(properties[key]);
7883
7883
  return result;
7884
7884
  }
7885
+ function FromRecordKey(type) {
7886
+ return String2({ pattern: RecordPattern(type) });
7887
+ }
7885
7888
  function FromPriorityTypes(types) {
7886
7889
  return FromTypes6(Priority(types));
7887
7890
  }
@@ -7889,7 +7892,7 @@ function FromTypes6(types) {
7889
7892
  return types.map((type) => FromType20(type));
7890
7893
  }
7891
7894
  function FromType20(type) {
7892
- const next = IsArray2(type) ? _Array_(FromType20(type.items), ArrayOptions(type)) : IsIntersect(type) ? Intersect(FromTypes6(type.allOf)) : IsUnion(type) ? Union(FromPriorityTypes(type.anyOf)) : IsObject2(type) ? _Object_(FromProperties4(type.properties)) : IsRecord(type) ? Record(RecordKey(type), FromType20(RecordValue(type))) : IsTuple(type) ? Tuple(FromTypes6(type.items)) : type;
7895
+ const next = IsArray2(type) ? _Array_(FromType20(type.items), ArrayOptions(type)) : IsIntersect(type) ? Intersect(FromTypes6(type.allOf)) : IsUnion(type) ? Union(FromPriorityTypes(type.anyOf)) : IsObject2(type) ? _Object_(FromProperties4(type.properties)) : IsRecord(type) ? Record(FromRecordKey(type), FromType20(RecordValue(type))) : IsTuple(type) ? Tuple(FromTypes6(type.items)) : type;
7893
7896
  return Modifiers(type, next);
7894
7897
  }
7895
7898
  function UnionPrioritySort(type) {
@@ -9164,7 +9167,7 @@ var ACTION_POLICY = {
9164
9167
  deploy: { write: ["deploy"] },
9165
9168
  applications: {
9166
9169
  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"],
9167
- write: ["upload_release", "put_configuration", "release_application", "transfer_release", "promote_application", "reconcile_promotion", "create_preview", "reconcile_preview", "cleanup_preview", "activate_release", "deploy_release", "rollback_release", "reconcile_activation", "retire_activation"]
9170
+ 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"]
9168
9171
  },
9169
9172
  project: {
9170
9173
  read: ["get", "endpoints", "health", "logs", "api_keys", "settings", "tasks", "task_detail", "task_stats", "dlq", "background_settings"],
@@ -9257,6 +9260,8 @@ var ACTION_POLICY = {
9257
9260
  write: ["send", "receive", "ack", "release", "fail", "retry", "delete_message", "update_settings"]
9258
9261
  },
9259
9262
  ai: { local: ["show_skill", "install_skill"] },
9263
+ init: { local: ["init"] },
9264
+ env: { local: ["list", "check"] },
9260
9265
  app: {
9261
9266
  local: ["init", "generate", "dev", "watch", "verify-plan", "compile", "check", "graph", "explain", "export-tools", "context", "doctor", "fix", "plan", "build"],
9262
9267
  read: ["status", "logs", "transfer-plan", "promote-plan", "rollback-plan", "history", "deploy-plan", "diff", "promote-status", "preview-plan", "previews", "preview-list", "preview-status"],
@@ -10488,34 +10493,67 @@ function parseApplicationDeployPlan(value) {
10488
10493
  throw new Error("Invalid application deploy plan action");
10489
10494
  return value;
10490
10495
  }
10491
- // node_modules/@supacloud/delivery/src/application-release-operation.ts
10496
+ // node_modules/@supacloud/delivery/src/application-deploy-operation.ts
10492
10497
  var closed3 = { additionalProperties: false };
10498
+ var sha256 = ApplicationReleaseIdSchema;
10493
10499
  var nullableActivation = Union([ApplicationActivationIdSchema, Null()]);
10494
- var ApplicationReleaseRequestSchema = _Object_({
10495
- release_id: ApplicationReleaseIdSchema,
10500
+ var ApplicationDeployWebRequestSchema = _Object_({
10501
+ deployment_id: String2({ pattern: "^[A-Za-z0-9_-]{1,128}$" }),
10502
+ release_id: sha256,
10503
+ expected_active_release_id: String2({ minLength: 1 }),
10504
+ expected_activation_id: String2({ minLength: 1 }),
10505
+ mutation_id: ApplicationActivationIdSchema
10506
+ }, closed3);
10507
+ var ApplicationDeployRequestSchema = _Object_({
10508
+ mutation_id: ApplicationActivationIdSchema,
10509
+ release_id: sha256,
10496
10510
  configuration_id: Optional(ApplicationConfigurationIdSchema),
10497
10511
  configuration_write: Optional(ApplicationConfigurationWriteSchema),
10498
- activation_id: Optional(ApplicationActivationIdSchema),
10499
- expected_activation_id: Optional(nullableActivation),
10500
- expected_plan_sha256: Optional(ApplicationReleaseIdSchema)
10512
+ expected_plan_sha256: Optional(sha256),
10513
+ approved_migration_digest: Optional(sha256),
10514
+ web: Optional(ApplicationDeployWebRequestSchema)
10501
10515
  }, closed3);
10502
- var ApplicationReleaseResultSchema = _Object_({
10503
- schema: Literal("supacloud.application-release.v1"),
10516
+ var ApplicationBackendReceiptSchema = _Object_({
10517
+ schema: Literal("supacloud.application-backend-receipt.v1"),
10504
10518
  project_ref: String2({ pattern: "^[a-z0-9-]{1,20}$" }),
10505
10519
  application_id: ApplicationIdSchema,
10506
10520
  environment_id: ApplicationIdSchema,
10507
- release_id: ApplicationReleaseIdSchema,
10521
+ release_id: sha256,
10522
+ manifest_sha256: sha256,
10523
+ mutation_id: ApplicationActivationIdSchema,
10524
+ plan_sha256: sha256,
10525
+ backup_id: Union([String2(), Null()]),
10526
+ before_ledger_digest: sha256,
10527
+ after_ledger_digest: sha256,
10528
+ applied_versions: _Array_(String2({ pattern: "^\\d{1,20}$" }), { uniqueItems: true }),
10529
+ schema_reloaded: Boolean2(),
10530
+ postgrest_healthy: Boolean2()
10531
+ }, closed3);
10532
+ var ApplicationDeployResultSchema = _Object_({
10533
+ schema: Literal("supacloud.application-deploy.v1"),
10534
+ project_ref: String2({ pattern: "^[a-z0-9-]{1,20}$" }),
10535
+ application_id: ApplicationIdSchema,
10536
+ environment_id: ApplicationIdSchema,
10537
+ mutation_id: ApplicationActivationIdSchema,
10538
+ release_id: sha256,
10508
10539
  configuration_id: ApplicationConfigurationIdSchema,
10509
10540
  activation_id: ApplicationActivationIdSchema,
10510
10541
  expected_activation_id: nullableActivation,
10511
- plan_sha256: ApplicationReleaseIdSchema,
10542
+ plan_sha256: sha256,
10512
10543
  action: Union([Literal("activate"), Literal("no-op")]),
10513
10544
  replayed: Boolean2(),
10514
- execution_performed: Boolean2()
10545
+ execution_performed: Boolean2(),
10546
+ backend_receipt: ApplicationBackendReceiptSchema,
10547
+ web: Optional(_Object_({
10548
+ deployment_id: String2({ pattern: "^[A-Za-z0-9_-]{1,128}$" }),
10549
+ release_id: sha256,
10550
+ activation_id: ApplicationActivationIdSchema,
10551
+ replayed: Boolean2()
10552
+ }, closed3))
10515
10553
  }, closed3);
10516
- function parseApplicationReleaseResult(value) {
10517
- if (!Check2(ApplicationReleaseResultSchema, value)) {
10518
- throw new Error("Invalid application release result");
10554
+ function parseApplicationDeployResult(value) {
10555
+ if (!Check2(ApplicationDeployResultSchema, value)) {
10556
+ throw new Error("Invalid application deploy result");
10519
10557
  }
10520
10558
  return structuredClone(value);
10521
10559
  }
@@ -10716,7 +10754,7 @@ function parseApplicationReleaseTransferResult(candidate) {
10716
10754
  }
10717
10755
  // node_modules/@supacloud/delivery/src/deployment-evidence.ts
10718
10756
  var closed5 = { additionalProperties: false };
10719
- var sha256 = String2({ pattern: "^[a-f0-9]{64}$" });
10757
+ var sha2562 = String2({ pattern: "^[a-f0-9]{64}$" });
10720
10758
  var isoDate = String2({ minLength: 20, maxLength: 40 });
10721
10759
  var status = Union([
10722
10760
  Literal("confirmed"),
@@ -10736,7 +10774,7 @@ var DatabaseProviderSchema = _Object_({
10736
10774
  topology: Union([Literal("single-node"), Literal("distributed")]),
10737
10775
  migration: _Object_({
10738
10776
  status,
10739
- inventory_sha256: Union([sha256, Null()]),
10777
+ inventory_sha256: Union([sha2562, Null()]),
10740
10778
  compatibility: Union([
10741
10779
  Literal("verified"),
10742
10780
  Literal("not-proven"),
@@ -10784,9 +10822,9 @@ var DeploymentEvidenceSchema = _Object_({
10784
10822
  }, closed5),
10785
10823
  source: _Object_({
10786
10824
  commit_sha: Union([String2({ pattern: "^[0-9a-f]{7,64}$" }), Null()]),
10787
- manifest_sha256: sha256,
10825
+ manifest_sha256: sha2562,
10788
10826
  contract_schema: Union([String2({ minLength: 1, maxLength: 128 }), Null()]),
10789
- environment_binding_version: Union([sha256, Null()])
10827
+ environment_binding_version: Union([sha2562, Null()])
10790
10828
  }, closed5),
10791
10829
  database: DatabaseProviderSchema,
10792
10830
  components: _Array_(DeploymentComponentEvidenceSchema, { minItems: 1, maxItems: 32 }),
@@ -10842,9 +10880,9 @@ function parseDeploymentEvidence(value) {
10842
10880
  // node_modules/@supacloud/delivery/src/application-promotion.ts
10843
10881
  var closed6 = { additionalProperties: false };
10844
10882
  var projectRef2 = String2({ pattern: "^[a-z0-9-]{1,20}$" });
10845
- var sha2562 = ApplicationReleaseIdSchema;
10883
+ var sha2563 = ApplicationReleaseIdSchema;
10846
10884
  var nullableActivation2 = Union([ApplicationActivationIdSchema, Null()]);
10847
- var nullableDigest = Union([sha2562, Null()]);
10885
+ var nullableDigest = Union([sha2563, Null()]);
10848
10886
  var observation = {
10849
10887
  activation_id: nullableActivation2,
10850
10888
  receipt_confirmed: Boolean2(),
@@ -10870,24 +10908,24 @@ var ApplicationPromotionPlanSchema = _Object_({
10870
10908
  project_ref: projectRef2,
10871
10909
  application_id: ApplicationIdSchema,
10872
10910
  environment_id: ApplicationIdSchema,
10873
- manifest_sha256: sha2562,
10874
- plan_sha256: sha2562,
10911
+ manifest_sha256: sha2563,
10912
+ plan_sha256: sha2563,
10875
10913
  source: _Object_({
10876
10914
  project_ref: projectRef2,
10877
10915
  environment_id: ApplicationIdSchema,
10878
- release_id: sha2562,
10916
+ release_id: sha2563,
10879
10917
  ...observation
10880
10918
  }, closed6),
10881
10919
  target: _Object_({
10882
- candidate_release_id: sha2562,
10883
- current_release_id: Union([sha2562, Null()]),
10920
+ candidate_release_id: sha2563,
10921
+ current_release_id: Union([sha2563, Null()]),
10884
10922
  artifact_action: Union([Literal("materialize"), Literal("reuse")]),
10885
10923
  configuration_id: Union([ApplicationConfigurationIdSchema, Null()]),
10886
10924
  configuration: Union([ApplicationConfigurationViewSchema, Null()]),
10887
10925
  ...observation
10888
10926
  }, closed6),
10889
10927
  migrations: _Object_({
10890
- ledger_digest: sha2562,
10928
+ ledger_digest: sha2563,
10891
10929
  ledger_compatible: Boolean2(),
10892
10930
  project_migrations_applied: Boolean2(),
10893
10931
  pending_versions: _Array_(String2({ pattern: "^\\d{1,20}$" }), { maxItems: 4096, uniqueItems: true }),
@@ -16852,7 +16890,8 @@ var ApplicationActivationRetirementResultSchema2 = ApplicationActivationRetireme
16852
16890
  var ApplicationRollbackSnapshotSchema2 = ApplicationRollbackSnapshotSchema;
16853
16891
  var ApplicationActivationHistoryCursorSchema2 = ApplicationActivationHistoryCursorSchema;
16854
16892
  var ApplicationPromotionPlanSchema2 = ApplicationPromotionPlanSchema;
16855
- var ApplicationReleaseRequestSchema2 = ApplicationReleaseRequestSchema;
16893
+ var ApplicationDeployRequestSchema2 = ApplicationDeployRequestSchema;
16894
+ var ApplicationDeployWebRequestSchema2 = ApplicationDeployWebRequestSchema;
16856
16895
 
16857
16896
  // src/shared/mutation-protocol.ts
16858
16897
  import { createHash as createHash6 } from "node:crypto";
@@ -17640,7 +17679,7 @@ var APPLICATION_TOOL_SCHEMA = {
17640
17679
  "put_configuration",
17641
17680
  "get_deploy_plan",
17642
17681
  "deploy_release",
17643
- "release_application",
17682
+ "deploy_application",
17644
17683
  "get_release_transfer_plan",
17645
17684
  "transfer_release",
17646
17685
  "get_promotion_plan",
@@ -17668,14 +17707,15 @@ var APPLICATION_TOOL_SCHEMA = {
17668
17707
  activation_id: optional(ApplicationActivationIdSchema2, "[deploy_release/activate_release/reconcile_activation/retire_activation/rollback_release/get_history] Stable activation ID; generated for automatic deployment/rollback if omitted"),
17669
17708
  expected_activation_id: optional(Union([ApplicationActivationIdSchema2, Null(), Literal("absent")]), "[deploy_release/activate_release/rollback_release] Current activation ID, or absent for first activation; selected from verified plan for automatic deployment"),
17670
17709
  configuration_path: optional(String2(), "[put_configuration] Local configuration write JSON including revision and expected revision"),
17671
- configuration_write: optional(Unknown(), "[release_application] Parsed immutable configuration write"),
17710
+ configuration_write: optional(Unknown(), "[deploy_application] Parsed immutable configuration write"),
17672
17711
  manifest_path: optional(String2(), "[upload_release] Local delivery.manifest.json"),
17712
+ web: optional(ApplicationDeployWebRequestSchema2, "[deploy_application] Immutable staged Web release binding"),
17673
17713
  release_id: optional(ApplicationReleaseIdSchema2, "[get_release/get_deploy_plan/deploy_release/activate_release/reconcile_activation/rollback_release] Immutable application release ID; platform selects previous for default rollback"),
17674
17714
  source_ref: optional(String2({ pattern: "^[A-Za-z0-9_-]{1,20}$" }), "[get_release_transfer_plan/transfer_release] Source project ref"),
17675
17715
  source_release_id: optional(ApplicationReleaseIdSchema2, "[get_release_transfer_plan/transfer_release] Source immutable release ID"),
17676
17716
  source_environment_id: optional(ApplicationIdSchema2, "[get_promotion_plan] Source environment"),
17677
- mutation_id: optional(ApplicationActivationIdSchema2, "[promote_application/get_promotion_status/reconcile_promotion] Stable promotion mutation ID"),
17678
- approved_migration_digest: optional(ApplicationReleaseIdSchema2, "[promote_application] Exact reviewed migration execution-plan digest"),
17717
+ mutation_id: optional(ApplicationActivationIdSchema2, "[deploy_application/promote_application/get_promotion_status/reconcile_promotion] Stable mutation ID"),
17718
+ approved_migration_digest: optional(ApplicationReleaseIdSchema2, "[deploy_application/promote_application] Exact reviewed migration execution-plan digest"),
17679
17719
  plan: optional(ApplicationPromotionPlanSchema2, "[promote_application] Immutable plan returned by get_promotion_plan"),
17680
17720
  cursor: optional(Union([ApplicationReleaseIdSchema2, ApplicationActivationHistoryCursorSchema2]), "[list_releases/get_history] Returned page cursor"),
17681
17721
  limit: optional(Integer({ minimum: 1, maximum: 100 }), "[list_releases/get_history] Page size, default 50/20"),
@@ -18028,57 +18068,50 @@ function registerApplicationTools(server, http) {
18028
18068
  const observed = await readDeployPlan(http, args, project);
18029
18069
  return observed.failure ?? releaseControlSuccess(operation, { ...observed.identity, plan: observed.plan });
18030
18070
  }
18031
- if (action === "release_application") {
18071
+ if (action === "deploy_application") {
18032
18072
  const environmentId = text(args, "environment_id");
18033
18073
  const releaseId = text(args, "release_id");
18034
- if (!Check2(ApplicationIdSchema2, environmentId) || !Check2(ApplicationReleaseIdSchema2, releaseId)) {
18035
- throw new Error("Invalid application release identity");
18074
+ const mutationId = text(args, "mutation_id");
18075
+ if (!Check2(ApplicationIdSchema2, environmentId) || !Check2(ApplicationReleaseIdSchema2, releaseId) || !Check2(ApplicationActivationIdSchema2, mutationId)) {
18076
+ throw new Error("Invalid application deploy identity");
18036
18077
  }
18037
18078
  const configurationId = args["configuration_id"];
18038
- const activationId = args["activation_id"];
18039
- const expectedActivationId = args["expected_activation_id"];
18040
18079
  if (configurationId !== undefined && !Check2(ApplicationConfigurationIdSchema2, configurationId)) {
18041
18080
  throw new Error("Invalid application configuration ID");
18042
18081
  }
18043
- if (activationId !== undefined && !Check2(ApplicationActivationIdSchema2, activationId)) {
18044
- throw new Error("Invalid application activation ID");
18045
- }
18046
- if (expectedActivationId !== undefined && expectedActivationId !== null && expectedActivationId !== "absent" && !Check2(ApplicationActivationIdSchema2, expectedActivationId)) {
18047
- throw new Error("Invalid expected activation ID");
18048
- }
18049
18082
  const configurationWrite = args["configuration_write"];
18050
18083
  const parsedConfigurationWrite = configurationWrite === undefined ? undefined : parseApplicationConfigurationWrite(configurationWrite);
18051
18084
  const requestedConfigurationId = configurationId ?? parsedConfigurationWrite?.configuration_id;
18052
18085
  const body = {
18086
+ mutation_id: mutationId,
18053
18087
  release_id: releaseId,
18054
18088
  ...configurationId === undefined ? {} : { configuration_id: configurationId },
18055
18089
  ...configurationWrite === undefined ? {} : { configuration_write: configurationWrite },
18056
- ...activationId === undefined ? {} : { activation_id: activationId },
18057
- ...expectedActivationId === undefined ? {} : {
18058
- expected_activation_id: expectedActivationId === "absent" ? null : expectedActivationId
18059
- }
18090
+ ...typeof args["approved_migration_digest"] === "string" ? { approved_migration_digest: args["approved_migration_digest"] } : {},
18091
+ ...args["web"] === undefined ? {} : { web: args["web"] }
18060
18092
  };
18061
- if (!Check2(ApplicationReleaseRequestSchema2, body)) {
18062
- throw new Error("Invalid application release request");
18093
+ if (!Check2(ApplicationDeployRequestSchema2, body)) {
18094
+ throw new Error("Invalid application deploy request");
18063
18095
  }
18064
- const result = await http.post(`/v1/projects/${project}/applications/${encodeURIComponent(id)}/environments/${encodeURIComponent(environmentId)}/release`, body, { timeoutMs: 120000, maxJsonBytes: 65536, responseTimeoutMs: 30000 });
18096
+ const result = await http.post(`/v1/projects/${project}/applications/${encodeURIComponent(id)}/environments/${encodeURIComponent(environmentId)}/deploy`, body, { timeoutMs: 120000, maxJsonBytes: 131072, responseTimeoutMs: 30000 });
18065
18097
  const identity = {
18066
18098
  project_ref: ref,
18067
18099
  application_id: id,
18068
18100
  environment_id: environmentId,
18069
18101
  release_id: releaseId,
18070
- ...configurationId === undefined ? {} : { configuration_id: configurationId },
18071
- ...activationId === undefined ? {} : { activation_id: activationId }
18102
+ mutation_id: mutationId,
18103
+ ...requestedConfigurationId === undefined ? {} : { configuration_id: requestedConfigurationId }
18072
18104
  };
18073
18105
  if (!result.ok)
18074
18106
  return releaseControlMutationFailure(operation, result, identity);
18075
18107
  try {
18076
18108
  if (result.status !== 200)
18077
18109
  throw new Error;
18078
- const release = parseApplicationReleaseResult(result.data);
18079
- if (release.project_ref !== ref || release.application_id !== id || release.environment_id !== environmentId || release.release_id !== releaseId || requestedConfigurationId !== undefined && release.configuration_id !== requestedConfigurationId || activationId !== undefined && release.activation_id !== activationId)
18110
+ const deploy = parseApplicationDeployResult(result.data);
18111
+ if (deploy.project_ref !== ref || deploy.application_id !== id || deploy.environment_id !== environmentId || deploy.release_id !== releaseId || deploy.mutation_id !== mutationId || requestedConfigurationId !== undefined && deploy.configuration_id !== requestedConfigurationId) {
18080
18112
  throw new Error;
18081
- return releaseControlSuccess(operation, release);
18113
+ }
18114
+ return releaseControlSuccess(operation, deploy);
18082
18115
  } catch {
18083
18116
  return releaseControlFailure(operation, "OUTCOME_UNKNOWN", result.status, identity);
18084
18117
  }
@@ -21107,25 +21140,25 @@ var starter_metadata_default = {
21107
21140
  generatedBy: "supacloud/starter-metadata-v1",
21108
21141
  packages: {
21109
21142
  compiler: {
21110
- version: "0.44.0"
21143
+ version: "0.44.1"
21111
21144
  },
21112
21145
  app: {
21113
- version: "0.26.0",
21146
+ version: "0.26.1",
21114
21147
  dependencies: {
21115
21148
  rxjs: "7.8.2"
21116
21149
  }
21117
21150
  },
21118
21151
  elysia: {
21119
- version: "0.29.1"
21152
+ version: "0.29.2"
21120
21153
  },
21121
21154
  commands: {
21122
- version: "0.9.1"
21155
+ version: "0.9.2"
21123
21156
  },
21124
21157
  contracts: {
21125
- version: "0.8.0"
21158
+ version: "0.8.1"
21126
21159
  },
21127
21160
  db: {
21128
- version: "0.16.0",
21161
+ version: "0.16.1",
21129
21162
  peerDependencies: {
21130
21163
  "drizzle-orm": "1.0.0-rc.5-169397b"
21131
21164
  },
@@ -21134,7 +21167,7 @@ var starter_metadata_default = {
21134
21167
  }
21135
21168
  },
21136
21169
  sdk: {
21137
- version: "0.41.1",
21170
+ version: "0.41.2",
21138
21171
  peerDependencies: {
21139
21172
  "@supabase/supabase-js": "^2.117.3"
21140
21173
  }
@@ -24533,6 +24566,7 @@ var REMOTE_APP_DESCRIPTIONS = {
24533
24566
  source_environment_id: "[promote-plan/diff/promote] Source environment ID",
24534
24567
  mutation_id: "[promote-status/promote-reconcile] Stable promotion mutation ID",
24535
24568
  approved_migration_digest: "[promote] Exact reviewed migration execution-plan digest",
24569
+ web: "[release] Immutable staged Web release binding prepared by the deploy profile",
24536
24570
  branch_ref: "[preview-plan] Preview branch identity",
24537
24571
  branch_name: "[preview/preview-create] Human-readable preview branch name",
24538
24572
  data_mode: "[preview-plan/preview/preview-create] schema_only or full_clone",
@@ -25501,9 +25535,6 @@ async function runAppRelease(request, options, delegate) {
25501
25535
  if (request.configuration_path !== undefined && request.configuration_id !== undefined) {
25502
25536
  throw new Error("app release accepts either --configuration_path or --configuration_id, not both");
25503
25537
  }
25504
- if (request.configuration_path === undefined && request.configuration_id === undefined) {
25505
- throw new Error("app release requires --configuration_path or --configuration_id");
25506
- }
25507
25538
  if (request.activation_id !== undefined || request.expected_activation_id !== undefined) {
25508
25539
  throw new Error("app release generates one activation identity; use app deploy for an exact retry");
25509
25540
  }
@@ -25557,25 +25588,36 @@ async function runAppRelease(request, options, delegate) {
25557
25588
  configurationWrite = parseApplicationConfigurationWrite(await Bun.file(configurationPath).json());
25558
25589
  configurationId = configurationWrite.configuration_id;
25559
25590
  } catch {
25560
- return releasePhaseFailure("configure", releaseControlFailure("applications.release_application", "INVALID_RESPONSE", null, { project_ref: ref, application_id: id, environment_id: environmentId }), completed);
25591
+ return releasePhaseFailure("configure", releaseControlFailure("applications.deploy_application", "INVALID_RESPONSE", null, { project_ref: ref, application_id: id, environment_id: environmentId }), completed);
25561
25592
  }
25562
25593
  completed.configure = { delegated: true, configuration_id: configurationId };
25563
25594
  } else {
25564
- completed.configure = { delegated: true, configuration_id: configurationId };
25595
+ completed.configure = {
25596
+ delegated: false,
25597
+ resolved_by: "platform",
25598
+ ...configurationId === undefined ? {} : { configuration_id: configurationId }
25599
+ };
25565
25600
  }
25566
25601
  const deployArgs = {
25567
- action: "release_application",
25602
+ action: "deploy_application",
25568
25603
  ref,
25569
25604
  id,
25570
25605
  environment_id: environmentId,
25571
25606
  release_id: releaseId,
25572
- configuration_id: configurationId,
25573
- activation_id: activationId,
25574
- ...configurationWrite === undefined ? {} : { configuration_write: configurationWrite }
25607
+ mutation_id: activationId,
25608
+ ...configurationId === undefined ? {} : { configuration_id: configurationId },
25609
+ ...configurationWrite === undefined ? {} : { configuration_write: configurationWrite },
25610
+ ...request.web === undefined ? {} : { web: request.web }
25575
25611
  };
25576
25612
  validateToolArguments(APPLICATION_TOOL_SCHEMA, deployArgs);
25577
25613
  const deployed = await delegate(deployArgs);
25578
25614
  const deployReceipt = resultObject(deployed);
25615
+ const resolvedConfigurationId = typeof deployReceipt?.configuration_id === "string" ? deployReceipt.configuration_id : undefined;
25616
+ if (resolvedConfigurationId !== undefined) {
25617
+ configurationId = resolvedConfigurationId;
25618
+ const configure = completed.configure;
25619
+ completed.configure = configure && typeof configure === "object" && !Array.isArray(configure) ? { ...configure, configuration_id: resolvedConfigurationId } : { delegated: false, resolved_by: "platform", configuration_id: resolvedConfigurationId };
25620
+ }
25579
25621
  if (deployed.isError || !deployReceipt?.ok) {
25580
25622
  return releasePhaseFailure("deploy", deployed, completed);
25581
25623
  }
@@ -25588,13 +25630,14 @@ async function runAppRelease(request, options, delegate) {
25588
25630
  application_id: id,
25589
25631
  environment_id: environmentId,
25590
25632
  release_id: releaseId,
25591
- configuration_id: configurationId,
25633
+ ...configurationId === undefined ? {} : { configuration_id: configurationId },
25592
25634
  manifest_path: manifestPath,
25593
25635
  build: buildReceipt ? completed.build : { skipped: true, manifest_path: manifestPath },
25594
25636
  phases: completed,
25595
25637
  no_op: deployReceipt.action === "no-op" || deployReceipt.no_op === true,
25596
25638
  execution_performed: deployReceipt.execution_performed === true,
25597
- ...typeof deployReceipt.activation_id === "string" ? { activation_id: deployReceipt.activation_id } : {}
25639
+ ...typeof deployReceipt.activation_id === "string" ? { activation_id: deployReceipt.activation_id } : {},
25640
+ ...deployReceipt.web === undefined ? {} : { web: deployReceipt.web }
25598
25641
  };
25599
25642
  return request.format === "json" || request.json === true ? textResult(JSON.stringify(result, null, 2)) : textResult([
25600
25643
  `${ref}/${id}/${environmentId}: ${result.no_op ? "no-op" : "released"}`,
@@ -27200,6 +27243,7 @@ import { createHash as createHash8 } from "node:crypto";
27200
27243
  import { existsSync as existsSync13, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "node:fs";
27201
27244
  import { dirname as dirname10, join as join16, relative as relative9, resolve as resolve21 } from "node:path";
27202
27245
  var SAFE_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
27246
+ var SAFE_ENVIRONMENT_ID = /^[A-Za-z0-9_-]{1,64}$/;
27203
27247
  var BACKUP_ID = /^logical-full_[A-Za-z0-9_-]{1,64}_[a-f0-9]{32}$/;
27204
27248
  var SHA256 = /^[a-f0-9]{64}$/;
27205
27249
  var SAFE_DATABASE = /^[^\u0000-\u001f\u007f]{1,128}$/;
@@ -27440,6 +27484,27 @@ function genericScopedReleaseScopeJson(scope) {
27440
27484
  function genericScopedReleaseScopeSha256(scope) {
27441
27485
  return createHash8("sha256").update(genericScopedReleaseScopeJson(scope)).digest("hex");
27442
27486
  }
27487
+ function scopeEnvironmentId(scope, action) {
27488
+ const environmentId = isRecord6(scope) ? scope.targetEnvironmentId : undefined;
27489
+ if (typeof environmentId !== "string" || !SAFE_ENVIRONMENT_ID.test(environmentId)) {
27490
+ throw new Error(`Scope for ${action} must be regenerated with a concrete targetEnvironmentId`);
27491
+ }
27492
+ return environmentId;
27493
+ }
27494
+ function requestedScopeEnvironment(args, action) {
27495
+ const environmentId = args.environment_id;
27496
+ if (typeof environmentId !== "string" || !SAFE_ENVIRONMENT_ID.test(environmentId)) {
27497
+ throw new Error(`'environment_id' is required for ${action} and must identify one deployment environment`);
27498
+ }
27499
+ return environmentId;
27500
+ }
27501
+ function assertScopeEnvironment(scope, requestedEnvironmentId, action) {
27502
+ const targetEnvironmentId = scopeEnvironmentId(scope, action);
27503
+ if (targetEnvironmentId !== requestedEnvironmentId) {
27504
+ throw new Error(`Scope target environment '${targetEnvironmentId}' does not match requested environment '${requestedEnvironmentId}'`);
27505
+ }
27506
+ return targetEnvironmentId;
27507
+ }
27443
27508
  function runGitRevParse(cwd, args) {
27444
27509
  const res = Bun.spawnSync(["git", ...args], { cwd });
27445
27510
  if (res.exitCode !== 0 || !res.stdout)
@@ -27566,10 +27631,10 @@ function collectScopeFiles(cwd, file, files, dir, task) {
27566
27631
  function handleScopeRebind(args) {
27567
27632
  const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
27568
27633
  const targetBaseCommit = resolveGitBaseCommit(cwd, args.base_commit);
27634
+ const requestedEnvironmentId = requestedScopeEnvironment(args, "scope_rebind");
27569
27635
  const scopeFiles = collectScopeFiles(cwd, args.file, args.files, args.dir, args.task);
27570
27636
  const dryRun = args.dry_run === true;
27571
- const scopes = [];
27572
- for (const filePath of scopeFiles) {
27637
+ const parsedScopes = scopeFiles.map((filePath) => {
27573
27638
  let content;
27574
27639
  try {
27575
27640
  content = readFileSync10(filePath, "utf8");
@@ -27582,15 +27647,20 @@ function handleScopeRebind(args) {
27582
27647
  } catch {
27583
27648
  throw new Error(`Scope file '${filePath}' is not valid JSON`);
27584
27649
  }
27585
- if (!isRecord6(parsed) || typeof parsed.baseCommit !== "string" || !/^[0-9a-f]{40}$/.test(parsed.baseCommit)) {
27650
+ const targetEnvironmentId = assertScopeEnvironment(parsed, requestedEnvironmentId, "scope_rebind");
27651
+ const previousBaseCommit = parsed.baseCommit;
27652
+ if (typeof previousBaseCommit !== "string" || !/^[0-9a-f]{40}$/.test(previousBaseCommit)) {
27586
27653
  throw new Error(`Scope file '${filePath}' has missing or invalid baseCommit`);
27587
27654
  }
27655
+ return { filePath, parsed, targetEnvironmentId };
27656
+ });
27657
+ const scopes = [];
27658
+ for (const { filePath, parsed, targetEnvironmentId } of parsedScopes) {
27588
27659
  const previousBaseCommit = parsed.baseCommit;
27589
27660
  const updated = previousBaseCommit !== targetBaseCommit;
27590
27661
  if (updated) {
27591
27662
  parsed.baseCommit = targetBaseCommit;
27592
27663
  }
27593
- const canonicalJson = genericScopedReleaseScopeJson(parsed);
27594
27664
  const scopeSha256 = genericScopedReleaseScopeSha256(parsed);
27595
27665
  if (updated && !dryRun) {
27596
27666
  const formatted = JSON.stringify(canonicalValue(parsed), null, 2) + `
@@ -27603,6 +27673,7 @@ function handleScopeRebind(args) {
27603
27673
  target_project_ref: typeof parsed.targetProjectRef === "string" ? parsed.targetProjectRef : "",
27604
27674
  previous_base_commit: previousBaseCommit,
27605
27675
  new_base_commit: targetBaseCommit,
27676
+ target_environment_id: targetEnvironmentId,
27606
27677
  scope_sha256: scopeSha256,
27607
27678
  updated
27608
27679
  });
@@ -27617,6 +27688,7 @@ function handleScopeRebind(args) {
27617
27688
  }
27618
27689
  function handleScopeInspect(args) {
27619
27690
  const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
27691
+ const requestedEnvironmentId = requestedScopeEnvironment(args, "scope_inspect");
27620
27692
  if (typeof args.file !== "string" || !args.file.trim()) {
27621
27693
  throw new Error("'file' is required for scope_inspect");
27622
27694
  }
@@ -27646,7 +27718,11 @@ function handleScopeInspect(args) {
27646
27718
  } catch {
27647
27719
  throw new Error(`Scope file '${args.file}' is not valid JSON`);
27648
27720
  }
27649
- if (!isRecord6(parsed) || typeof parsed.baseCommit !== "string" || !/^[0-9a-f]{40}$/.test(parsed.baseCommit)) {
27721
+ if (!isRecord6(parsed)) {
27722
+ throw new Error(`Scope file '${args.file}' must be an object and must be regenerated`);
27723
+ }
27724
+ const targetEnvironmentId = assertScopeEnvironment(parsed, requestedEnvironmentId, "scope_inspect");
27725
+ if (typeof parsed.baseCommit !== "string" || !/^[0-9a-f]{40}$/.test(parsed.baseCommit)) {
27650
27726
  throw new Error(`Scope file '${args.file}' has missing or invalid baseCommit`);
27651
27727
  }
27652
27728
  const scopeSha256 = genericScopedReleaseScopeSha256(parsed);
@@ -27656,6 +27732,7 @@ function handleScopeInspect(args) {
27656
27732
  scope: {
27657
27733
  task_id: parsed.taskId,
27658
27734
  target_project_ref: parsed.targetProjectRef,
27735
+ target_environment_id: targetEnvironmentId,
27659
27736
  base_commit: parsed.baseCommit,
27660
27737
  functions: parsed.functions,
27661
27738
  excluded_functions: parsed.excludedFunctions,
@@ -27693,6 +27770,7 @@ function handleScopeCreate(args, fallbackRef) {
27693
27770
  if (typeof args.task !== "string" || !args.task.trim()) {
27694
27771
  throw new Error("'task' is required for scope_create");
27695
27772
  }
27773
+ const targetEnvironmentId = requestedScopeEnvironment(args, "scope_create");
27696
27774
  const projectRef = typeof args.ref === "string" && args.ref.trim() || fallbackRef;
27697
27775
  if (!projectRef) {
27698
27776
  throw new Error("'ref' is required for scope_create");
@@ -27709,6 +27787,7 @@ function handleScopeCreate(args, fallbackRef) {
27709
27787
  functions: parseCommaList(args.functions),
27710
27788
  migrations: parseCommaList(args.migrations),
27711
27789
  targetProjectRef: projectRef,
27790
+ targetEnvironmentId,
27712
27791
  taskId: String(args.task).trim(),
27713
27792
  web: Boolean(args.web)
27714
27793
  };
@@ -27721,6 +27800,7 @@ function handleScopeCreate(args, fallbackRef) {
27721
27800
  file: relative9(cwd, filePath),
27722
27801
  task_id: scope.taskId,
27723
27802
  target_project_ref: projectRef,
27803
+ target_environment_id: targetEnvironmentId,
27724
27804
  base_commit: targetBaseCommit,
27725
27805
  scope_sha256: scopeSha256,
27726
27806
  scope
@@ -27747,6 +27827,7 @@ function registerReleaseTools(server, http, options = {}) {
27747
27827
  dir: optional(String2(), "[scope_rebind] Directory containing release scope JSON files (defaults to supacloud/fa/release-scopes, release-scopes, or scopes)"),
27748
27828
  task: optional(String2(), "[scope_rebind/scope_create] Task ID or prefix filter for scope files"),
27749
27829
  base_commit: optional(String2(), "[scope_rebind/scope_create] Target 40-character base commit SHA or git ref (defaults to origin/main -> main -> HEAD^ -> HEAD)"),
27830
+ environment_id: optional(String2(), "[scope_inspect/scope_rebind/scope_create] Exact target deployment environment"),
27750
27831
  functions: optional(String2(), "[scope_create] Comma-separated list of functions to deploy"),
27751
27832
  excluded_functions: optional(String2(), "[scope_create] Comma-separated list of functions to exclude"),
27752
27833
  migrations: optional(String2(), "[scope_create] Comma-separated list of migrations to apply"),
@@ -28148,6 +28229,33 @@ function selectDeployment(deployments, requestedId, projectName) {
28148
28229
  const ids = deployments.map((deployment) => deployment.id).sort().join(", ");
28149
28230
  throw new Error(deployments.length === 0 ? "No frontend deployments exist for this project" : `Multiple frontend deployments found (${ids}). Set frontend.id in supacloud.json or pass --id`);
28150
28231
  }
28232
+ function selectFrontendTarget(configRoot, config) {
28233
+ if (config.targets === undefined && config.frontend === undefined)
28234
+ return null;
28235
+ const targets = config.targets ? Object.entries(config.targets).filter(([, target]) => target.type === "frontend") : [["frontend", {
28236
+ type: "frontend",
28237
+ root: config.frontend?.root,
28238
+ id: config.frontend?.id,
28239
+ buildCommand: config.frontend?.buildCommand,
28240
+ outputDirectory: config.frontend?.outputDirectory,
28241
+ buildEnv: config.frontend?.buildEnv
28242
+ }]];
28243
+ if (targets.length === 0)
28244
+ return null;
28245
+ if (config.defaultTarget) {
28246
+ const selected = targets.find(([name]) => name === config.defaultTarget);
28247
+ if (selected)
28248
+ return { name: selected[0], config: selected[1], root: targetRoot(configRoot, selected[1], selected[0]) };
28249
+ }
28250
+ const namedWeb = targets.find(([name]) => name === "web");
28251
+ if (namedWeb)
28252
+ return { name: namedWeb[0], config: namedWeb[1], root: targetRoot(configRoot, namedWeb[1], namedWeb[0]) };
28253
+ if (targets.length === 1) {
28254
+ const selected = targets[0];
28255
+ return { name: selected[0], config: selected[1], root: targetRoot(configRoot, selected[1], selected[0]) };
28256
+ }
28257
+ throw new Error(`Multiple frontend deploy targets found (${targets.map(([name]) => name).sort().join(", ")}). Set defaultTarget to the Web target`);
28258
+ }
28151
28259
  function targetRoot(configRoot, target, name) {
28152
28260
  const root = resolve22(configRoot, target.root || (name === "frontend" ? "." : name));
28153
28261
  const prefix = `${resolve22(configRoot)}${sep5}`;
@@ -28320,6 +28428,71 @@ function payload(response) {
28320
28428
  throw new Error("Deployment operation returned an invalid response");
28321
28429
  return parsed;
28322
28430
  }
28431
+ async function stageApplicationWebRelease(http, projectRef, configRoot, skipBuild, json, environmentVariables) {
28432
+ const target = selectFrontendTarget(configRoot, await readDeployConfig(configRoot));
28433
+ if (!target)
28434
+ return null;
28435
+ const projectDirectory = target.root;
28436
+ const targetState = await lstat5(projectDirectory).catch(() => null);
28437
+ if (!targetState?.isDirectory() || targetState.isSymbolicLink()) {
28438
+ throw new Error(`Deploy target '${target.name}' root is not a regular directory: ${projectDirectory}`);
28439
+ }
28440
+ const requestedId = target.config.id;
28441
+ const listResponse = await http.get(`/v1/projects/${encodeURIComponent(projectRef)}/frontend/deployments`);
28442
+ if (!listResponse.ok)
28443
+ throw new Error(`Failed to list frontend deployments (HTTP ${listResponse.status})`);
28444
+ const selected = selectDeployment(deploymentList(listResponse.data), requestedId, await packageName(projectDirectory));
28445
+ const configuredBuildCommand = String(target.config.buildCommand || selected.buildCommand || "").trim();
28446
+ const buildCommand = configuredBuildCommand || (skipBuild ? "" : await defaultBuildCommand(projectDirectory, configRoot));
28447
+ const configuredOutput = String(target.config.outputDirectory || "").trim();
28448
+ const remoteOutput = selected.outputDirectory && selected.outputDirectory !== "." ? selected.outputDirectory : "";
28449
+ const outputDirectory = resolveInsideProject(projectDirectory, configuredOutput || remoteOutput || defaultOutputDirectory(selected.framework));
28450
+ const build = createBuildEnvironment(target.config.buildEnv, environmentVariables);
28451
+ const report = phaseReporter(json);
28452
+ report("web", `${target.name} (${selected.id})`);
28453
+ assertBuildEnvironment(build, skipBuild);
28454
+ if (!skipBuild) {
28455
+ report("build", buildCommand);
28456
+ await runBuild(buildCommand, projectDirectory, build.env, json);
28457
+ } else {
28458
+ report("build", "skipped");
28459
+ }
28460
+ report("package", relative10(projectDirectory, outputDirectory) || ".");
28461
+ const archive = await createFrontendArchive(outputDirectory);
28462
+ try {
28463
+ const authority = payload(await readFrontendReleaseAuthority(http, projectRef, selected.id));
28464
+ const activeReleaseId = typeof authority.active_release_id === "string" ? authority.active_release_id : null;
28465
+ const activeActivationId = typeof authority.active_activation_id === "string" ? authority.active_activation_id : null;
28466
+ const current = Array.isArray(authority.releases) ? record4(authority.releases[0]) : null;
28467
+ if (archive.sha256 === activeReleaseId && archive.treeSha256 !== current?.tree_sha256) {
28468
+ throw new Error("Active Web release tree does not match the local archive");
28469
+ }
28470
+ if (archive.treeSha256 === current?.tree_sha256) {
28471
+ report("done", "Web already current");
28472
+ return {};
28473
+ }
28474
+ report("upload", `${archive.fileCount} files`);
28475
+ const uploaded = payload(await uploadFrontendRelease(http, projectRef, selected.id, archive.archivePath));
28476
+ const release = record4(uploaded.release);
28477
+ const releaseId = requiredString(release?.release_id, "release.release_id");
28478
+ if (releaseId !== archive.sha256)
28479
+ throw new Error("Uploaded Web release identity does not match the local archive");
28480
+ if (release?.tree_sha256 !== archive.treeSha256)
28481
+ throw new Error("Uploaded Web release tree does not match the local build");
28482
+ report("done", releaseId.slice(0, 12));
28483
+ return {
28484
+ request: {
28485
+ deployment_id: selected.id,
28486
+ release_id: releaseId,
28487
+ expected_active_release_id: activeReleaseId ?? "absent",
28488
+ expected_activation_id: activeActivationId ?? "absent",
28489
+ mutation_id: crypto.randomUUID()
28490
+ }
28491
+ };
28492
+ } finally {
28493
+ await archive.cleanup();
28494
+ }
28495
+ }
28323
28496
  function phaseReporter(json) {
28324
28497
  const started = performance.now();
28325
28498
  return (phase, detail) => {
@@ -28368,12 +28541,10 @@ function registerDeployTools(server, http, options = {}) {
28368
28541
  }
28369
28542
  const configurationPath = options.application.configurationPath?.trim();
28370
28543
  const configurationId = options.application.configurationId?.trim();
28371
- if (!configurationPath && !configurationId) {
28372
- throw new Error("Application profile requires SUPACLOUD_APPLICATION_CONFIGURATION_PATH or SUPACLOUD_APPLICATION_CONFIGURATION_ID");
28373
- }
28374
28544
  if (configurationPath && configurationId) {
28375
28545
  throw new Error("Application profile must set only one of SUPACLOUD_APPLICATION_CONFIGURATION_PATH or SUPACLOUD_APPLICATION_CONFIGURATION_ID");
28376
28546
  }
28547
+ const stagedWeb = await stageApplicationWebRelease(http, projectRef, configRoot, args.skip_build === true, args.json === true, options.environmentVariables);
28377
28548
  const result = await options.applicationRelease({
28378
28549
  action: "release",
28379
28550
  ref: projectRef,
@@ -28384,6 +28555,7 @@ function registerDeployTools(server, http, options = {}) {
28384
28555
  ...configurationId ? { configuration_id: configurationId } : {},
28385
28556
  ...options.application.manifestPath ? { manifest_path: options.application.manifestPath } : {},
28386
28557
  ...args.skip_build === true ? { skip_build: true } : {},
28558
+ ...stagedWeb?.request === undefined ? {} : { web: stagedWeb.request },
28387
28559
  ...args.json === true ? { format: "json", json: true } : {}
28388
28560
  });
28389
28561
  return result;
@@ -28887,10 +29059,320 @@ function registerRemoteDevTools(server, options = {}) {
28887
29059
  throw new Error("Unsupported remote dev action");
28888
29060
  });
28889
29061
  }
29062
+
29063
+ // src/shared/tools/developer-experience-tools.ts
29064
+ import { existsSync as existsSync15 } from "node:fs";
29065
+ import { mkdir as mkdir7, readdir as readdir8, readFile as readFile7, writeFile as writeFile6 } from "node:fs/promises";
29066
+ import { dirname as dirname12, join as join19, resolve as resolve24 } from "node:path";
29067
+ var initToolSchema = {
29068
+ root: optional(String2(), "Project directory to initialize (default: current directory)"),
29069
+ json: optional(Boolean2(), "Emit one machine-readable JSON result")
29070
+ };
29071
+ var environmentToolSchema = {
29072
+ action: withDescription(stringEnum(["list", "check"]), "Environment profile inspection action"),
29073
+ root: optional(String2(), "Project directory containing .env.supacloud.* files"),
29074
+ json: optional(Boolean2(), "Emit one machine-readable JSON result")
29075
+ };
29076
+ var TEST_PROFILE = [
29077
+ "SUPACLOUD_ENV=test",
29078
+ "SUPACLOUD_API_URL=https://management.test.example.com",
29079
+ "SUPACLOUD_API_TOKEN=<test-management-api-token>",
29080
+ "SUPACLOUD_PROJECT_REF=<test-project-ref>",
29081
+ ""
29082
+ ].join(`
29083
+ `);
29084
+ var PRODUCTION_PROFILE = [
29085
+ "SUPACLOUD_ENV=production",
29086
+ "SUPACLOUD_API_URL=https://management.example.com",
29087
+ "SUPACLOUD_API_TOKEN=<production-management-api-token>",
29088
+ "SUPACLOUD_PROJECT_REF=<production-project-ref>",
29089
+ ""
29090
+ ].join(`
29091
+ `);
29092
+ function textResult3(text, isError = false) {
29093
+ return { isError, content: [{ type: "text", text }] };
29094
+ }
29095
+ function jsonResult(value, isError = false) {
29096
+ return textResult3(JSON.stringify(value, null, 2), isError);
29097
+ }
29098
+ function record5(value) {
29099
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
29100
+ }
29101
+ function packageDependencies(manifest) {
29102
+ return new Set([
29103
+ ...Object.keys(manifest.dependencies ?? {}),
29104
+ ...Object.keys(manifest.devDependencies ?? {})
29105
+ ]);
29106
+ }
29107
+ function outputDirectoryFor(manifest) {
29108
+ const dependencies = packageDependencies(manifest);
29109
+ if (dependencies.has("next"))
29110
+ return "out";
29111
+ if (dependencies.has("@sveltejs/kit") || dependencies.has("svelte"))
29112
+ return "build";
29113
+ return "dist";
29114
+ }
29115
+ async function readPackageManifest(root) {
29116
+ const path = join19(root, "package.json");
29117
+ if (!existsSync15(path))
29118
+ return {};
29119
+ try {
29120
+ const parsed = JSON.parse(await readFile7(path, "utf8"));
29121
+ const value = record5(parsed);
29122
+ if (!value)
29123
+ throw new Error("package.json must contain an object");
29124
+ const scripts = record5(value.scripts);
29125
+ const dependencies = record5(value.dependencies);
29126
+ const devDependencies = record5(value.devDependencies);
29127
+ return {
29128
+ scripts: scripts ?? undefined,
29129
+ dependencies: dependencies ?? undefined,
29130
+ devDependencies: devDependencies ?? undefined
29131
+ };
29132
+ } catch (error) {
29133
+ throw new Error(`Invalid package.json: ${error instanceof Error ? error.message : String(error)}`);
29134
+ }
29135
+ }
29136
+ async function writeNewFile(path, content) {
29137
+ try {
29138
+ await writeFile6(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
29139
+ return true;
29140
+ } catch (error) {
29141
+ if (error instanceof Error && "code" in error && error.code === "EEXIST")
29142
+ return false;
29143
+ throw error;
29144
+ }
29145
+ }
29146
+ async function ensureGitignore(root) {
29147
+ const path = join19(root, ".gitignore");
29148
+ const existing = existsSync15(path) ? await readFile7(path, "utf8") : "";
29149
+ const required = [".env.supacloud.*", "!.env.supacloud.*.example"];
29150
+ const missing = required.filter((entry) => !existing.split(/\r?\n/).includes(entry));
29151
+ if (missing.length === 0)
29152
+ return false;
29153
+ const prefix = existing.length > 0 && !existing.endsWith(`
29154
+ `) ? `${existing}
29155
+ ` : existing;
29156
+ await writeFile6(path, `${prefix}${missing.join(`
29157
+ `)}
29158
+ `, "utf8");
29159
+ return true;
29160
+ }
29161
+ async function initializeProject(request, options) {
29162
+ const root = resolve24(request.root || options.cwd || process.cwd());
29163
+ await mkdir7(root, { recursive: true });
29164
+ const configPath = join19(root, "supacloud.json");
29165
+ if (existsSync15(configPath)) {
29166
+ throw new Error(`supacloud.json already exists in ${root}; init never overwrites project configuration`);
29167
+ }
29168
+ const manifest = await readPackageManifest(root);
29169
+ const hasBuildScript = typeof manifest.scripts?.build === "string" && Boolean(manifest.scripts.build.trim());
29170
+ const outputDirectory = outputDirectoryFor(manifest);
29171
+ const config = {
29172
+ frontend: {
29173
+ root: ".",
29174
+ outputDirectory
29175
+ }
29176
+ };
29177
+ const created = [];
29178
+ try {
29179
+ await writeFile6(configPath, `${JSON.stringify(config, null, 2)}
29180
+ `, {
29181
+ encoding: "utf8",
29182
+ flag: "wx",
29183
+ mode: 420
29184
+ });
29185
+ } catch (error) {
29186
+ if (error instanceof Error && "code" in error && error.code === "EEXIST") {
29187
+ throw new Error(`supacloud.json already exists in ${root}; init never overwrites project configuration`);
29188
+ }
29189
+ throw error;
29190
+ }
29191
+ created.push("supacloud.json");
29192
+ if (await writeNewFile(join19(root, ".env.supacloud.test.example"), TEST_PROFILE)) {
29193
+ created.push(".env.supacloud.test.example");
29194
+ }
29195
+ if (await writeNewFile(join19(root, ".env.supacloud.production.example"), PRODUCTION_PROFILE)) {
29196
+ created.push(".env.supacloud.production.example");
29197
+ }
29198
+ if (await ensureGitignore(root))
29199
+ created.push(".gitignore");
29200
+ const result = {
29201
+ schema: "supacloud.developer-experience.init.v1",
29202
+ ok: true,
29203
+ root,
29204
+ created,
29205
+ detected: {
29206
+ package_json: existsSync15(join19(root, "package.json")),
29207
+ build_script: hasBuildScript,
29208
+ output_directory: outputDirectory
29209
+ },
29210
+ next: [
29211
+ "cp .env.supacloud.test.example .env.supacloud.test",
29212
+ "supacloud env check",
29213
+ "supacloud --env test deploy"
29214
+ ]
29215
+ };
29216
+ if (request.json === true)
29217
+ return jsonResult(result);
29218
+ return textResult3([
29219
+ `Initialized SupaCloud deployment in ${root}.`,
29220
+ `Created: ${created.join(", ")}.`,
29221
+ hasBuildScript ? `Detected package.json build script; deploy output defaults to ${outputDirectory}.` : "No package.json build script detected; add one before deploying.",
29222
+ "",
29223
+ ...result.next
29224
+ ].join(`
29225
+ `));
29226
+ }
29227
+ function profileNameFromFile(name) {
29228
+ const match = name.match(/^\.env\.supacloud\.([A-Za-z0-9][A-Za-z0-9_-]{0,63})$/);
29229
+ return match?.[1] ?? null;
29230
+ }
29231
+ async function environmentProfiles(root) {
29232
+ const entries = await readdir8(root, { withFileTypes: true }).catch((error) => {
29233
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
29234
+ return [];
29235
+ throw error;
29236
+ });
29237
+ return entries.filter((entry) => entry.isFile()).map((entry) => {
29238
+ const name = profileNameFromFile(entry.name);
29239
+ return name ? { name, path: join19(root, entry.name), exists: true } : null;
29240
+ }).filter((profile) => profile !== null).sort((left, right) => left.name.localeCompare(right.name));
29241
+ }
29242
+ function statusMissing(context) {
29243
+ if (context.credentialScope === "management") {
29244
+ return [
29245
+ !context.apiUrl ? "SUPACLOUD_API_URL" : null,
29246
+ !context.apiToken ? "SUPACLOUD_API_TOKEN" : null,
29247
+ !context.projectRef ? "SUPACLOUD_PROJECT_REF" : null
29248
+ ].filter((value) => value !== null);
29249
+ }
29250
+ if (context.credentialScope === "project_application") {
29251
+ return [
29252
+ !context.inferredSupabaseUrl ? "SUPABASE_URL" : null,
29253
+ !context.inferredServiceRoleKey ? "SUPABASE_SERVICE_ROLE_KEY" : null,
29254
+ !context.projectRef ? "SUPACLOUD_PROJECT_REF" : null
29255
+ ].filter((value) => value !== null);
29256
+ }
29257
+ return ["SUPACLOUD_API_URL", "SUPACLOUD_API_TOKEN", "SUPACLOUD_PROJECT_REF"];
29258
+ }
29259
+ function deployMissing(context) {
29260
+ const missing = statusMissing(context);
29261
+ if (context.credentialScope !== "management") {
29262
+ return [...missing, "Management API context"];
29263
+ }
29264
+ if (context.applicationId) {
29265
+ if (!context.applicationEnvironmentId)
29266
+ missing.push("SUPACLOUD_APPLICATION_ENVIRONMENT_ID");
29267
+ if (!context.applicationConfigurationPath && !context.applicationConfigurationId) {
29268
+ missing.push("SUPACLOUD_APPLICATION_CONFIGURATION_PATH or SUPACLOUD_APPLICATION_CONFIGURATION_ID");
29269
+ }
29270
+ }
29271
+ return missing;
29272
+ }
29273
+ async function inspectProfile(profile) {
29274
+ try {
29275
+ const context = resolveSupaCloudContext({}, dirname12(profile.path), {
29276
+ environmentName: profile.name
29277
+ });
29278
+ const status = statusMissing(context);
29279
+ const deploy = deployMissing(context);
29280
+ return {
29281
+ name: profile.name,
29282
+ path: profile.path,
29283
+ environment: context.environment || null,
29284
+ production: context.production,
29285
+ source: "named_env_file",
29286
+ credentialScope: context.credentialScope,
29287
+ projectRef: context.projectRef || null,
29288
+ statusReady: status.length === 0,
29289
+ statusMissing: status,
29290
+ deployReady: deploy.length === 0,
29291
+ deployMissing: deploy,
29292
+ hasApiToken: Boolean(context.apiToken),
29293
+ hasServiceRoleKey: Boolean(context.inferredServiceRoleKey),
29294
+ error: null
29295
+ };
29296
+ } catch (error) {
29297
+ return {
29298
+ name: profile.name,
29299
+ path: profile.path,
29300
+ environment: null,
29301
+ production: false,
29302
+ source: "invalid",
29303
+ credentialScope: "incomplete",
29304
+ projectRef: null,
29305
+ statusReady: false,
29306
+ statusMissing: [],
29307
+ deployReady: false,
29308
+ deployMissing: [],
29309
+ hasApiToken: false,
29310
+ hasServiceRoleKey: false,
29311
+ error: error instanceof Error ? error.message : String(error)
29312
+ };
29313
+ }
29314
+ }
29315
+ async function inspectEnvironment(request, options) {
29316
+ const root = resolve24(request.root || options.cwd || process.cwd());
29317
+ const profiles = await environmentProfiles(root);
29318
+ if (request.action === "list") {
29319
+ const active = options.activeContext?.environment || null;
29320
+ const result = {
29321
+ schema: "supacloud.developer-experience.environments.v1",
29322
+ ok: true,
29323
+ root,
29324
+ active,
29325
+ environments: profiles.map((profile) => ({
29326
+ name: profile.name,
29327
+ path: profile.path,
29328
+ active: profile.name.toLowerCase() === active?.toLowerCase()
29329
+ }))
29330
+ };
29331
+ if (request.json === true)
29332
+ return jsonResult(result);
29333
+ if (profiles.length === 0) {
29334
+ return textResult3(`No .env.supacloud.<name> profiles found in ${root}.
29335
+ Run \`supacloud init\` to create safe examples.`);
29336
+ }
29337
+ return textResult3([
29338
+ `Environment profiles in ${root}:`,
29339
+ ...result.environments.map((profile) => `${profile.active ? "*" : " "} ${profile.name} ${profile.path}`)
29340
+ ].join(`
29341
+ `));
29342
+ }
29343
+ const reports = await Promise.all(profiles.map((profile) => inspectProfile(profile)));
29344
+ const result = {
29345
+ schema: "supacloud.developer-experience.environment-check.v1",
29346
+ ok: reports.length > 0 && reports.every((report) => report.statusReady),
29347
+ root,
29348
+ active: options.activeContext?.environment || null,
29349
+ environments: reports
29350
+ };
29351
+ if (request.json === true)
29352
+ return jsonResult(result, !result.ok);
29353
+ if (reports.length === 0) {
29354
+ return textResult3(`No environment profiles found in ${root}.
29355
+ Run \`supacloud init\` and copy an example profile.`, true);
29356
+ }
29357
+ const lines = reports.map((report) => {
29358
+ if (report.error)
29359
+ return `FAIL ${report.name}: ${report.error}`;
29360
+ const status = report.statusReady ? "OK" : `FAIL missing ${report.statusMissing.join(", ")}`;
29361
+ const deploy = report.deployReady ? "deploy=ready" : `deploy=blocked(${report.deployMissing.join(", ")})`;
29362
+ const ref = report.projectRef ? `project=${report.projectRef}` : "project=unknown";
29363
+ return `${status} ${report.name} ${deploy} ${ref}`;
29364
+ });
29365
+ return textResult3(lines.join(`
29366
+ `), !result.ok);
29367
+ }
29368
+ function registerDeveloperExperienceTools(server, options = {}) {
29369
+ registerTool(server, "init", "Create SupaCloud deployment configuration and non-secret environment examples without overwriting project files.", initToolSchema, (request) => initializeProject(request, options));
29370
+ registerTool(server, "env", "List or locally validate named SupaCloud environment profiles without contacting the platform or printing secrets.", environmentToolSchema, (request) => inspectEnvironment(request, options));
29371
+ }
28890
29372
  // package.json
28891
29373
  var package_default = {
28892
29374
  name: "@supacloud/cli",
28893
- version: "0.87.1",
29375
+ version: "0.90.0",
28894
29376
  description: "Project-scoped CLI for SupaCloud users",
28895
29377
  type: "module",
28896
29378
  main: "./dist/index.js",
@@ -28942,14 +29424,14 @@ var package_default = {
28942
29424
  directory: "packages/cli"
28943
29425
  },
28944
29426
  dependencies: {
28945
- "@supacloud/compiler": "^0.44.0",
28946
- "@supacloud/delivery": "0.14.0",
28947
- "@supacloud/db": "0.16.0",
28948
- typebox: "1.3.34"
29427
+ "@supacloud/compiler": "^0.44.1",
29428
+ "@supacloud/delivery": "0.15.0",
29429
+ "@supacloud/db": "0.16.1",
29430
+ typebox: "1.3.36"
28949
29431
  },
28950
29432
  overrides: {
28951
- "@supacloud/contracts": "0.8.0",
28952
- "@supacloud/delivery": "0.14.0",
29433
+ "@supacloud/contracts": "0.8.1",
29434
+ "@supacloud/delivery": "0.15.0",
28953
29435
  "@graphql-tools/utils": "^12.0.3"
28954
29436
  },
28955
29437
  devDependencies: {
@@ -29125,6 +29607,8 @@ USAGE
29125
29607
 
29126
29608
  ${preferredCommand} [global flags] deploy [--flags]
29127
29609
  ${preferredCommand} [global flags] <module> <action> [--flags]
29610
+ ${preferredCommand} init [--root <directory>]
29611
+ ${preferredCommand} env <list|check> [--root <directory>]
29128
29612
  ${preferredCommand} [global flags] status
29129
29613
  ${preferredCommand} --help
29130
29614
  ${preferredCommand} --version
@@ -29157,6 +29641,9 @@ DEFAULT CONTEXT
29157
29641
 
29158
29642
  EXAMPLES
29159
29643
 
29644
+ ${preferredCommand} init
29645
+ ${preferredCommand} env check
29646
+ ${preferredCommand} env check --json
29160
29647
  ${preferredCommand} status
29161
29648
  ${preferredCommand} deploy
29162
29649
  ${preferredCommand} --env test deploy
@@ -29178,8 +29665,8 @@ EXAMPLES
29178
29665
  ${preferredCommand} release postgrest_restart --ref abc123
29179
29666
  ${preferredCommand} release release_canary_fixture_stage_replay --ref abc123 --subject <uuid> --request_id <uuid>
29180
29667
  ${preferredCommand} release release_canary_fixture_disable_replay --ref abc123 --fixture_id <uuid> --disable_request_id <uuid> --issuer <issuer-url> --subject <uuid>
29181
- ${preferredCommand} release scope_rebind --file supacloud/fa/release-scopes/20260907-intake-p1-production.json
29182
- ${preferredCommand} release scope_inspect --file supacloud/fa/release-scopes/20260907-intake-p1-production.json
29668
+ ${preferredCommand} release scope_rebind --file supacloud/fa/release-scopes/20260907-intake-p1-production.json --environment_id production
29669
+ ${preferredCommand} release scope_inspect --file supacloud/fa/release-scopes/20260907-intake-p1-production.json --environment_id production
29183
29670
  ${preferredCommand} queue stats --queue emails
29184
29671
  ${preferredCommand} queue dlq --queue emails --limit 20
29185
29672
  ${preferredCommand} frontend list --ref abc123
@@ -29316,6 +29803,10 @@ function createCliTools(context, confirmProduction) {
29316
29803
  })));
29317
29804
  Object.assign(tools, captureTools((server) => registerLiteCliTools(server)));
29318
29805
  Object.assign(tools, captureTools((server) => registerAiTools(server)));
29806
+ Object.assign(tools, captureTools((server) => registerDeveloperExperienceTools(server, {
29807
+ cwd: process.cwd(),
29808
+ activeContext: context
29809
+ })));
29319
29810
  Object.assign(tools, captureTools((server) => registerAppTools(server, { onDevProgress: writeDevProgress })));
29320
29811
  Object.assign(tools, captureTools((server) => registerAppAliases(server)));
29321
29812
  Object.assign(tools, captureTools((server) => registerDbGovernanceTools(server, {
@@ -29550,7 +30041,7 @@ async function main() {
29550
30041
  return;
29551
30042
  }
29552
30043
  const cliTools = createCliTools(context, globalOptions.confirmProduction);
29553
- if (args.length === 1 && !["ai", "supabase", "lite", "app", "db"].includes(args[0]) && cliTools[args[0]]) {
30044
+ if (args.length === 1 && !["ai", "supabase", "lite", "app", "db", "env"].includes(args[0]) && cliTools[args[0]]) {
29554
30045
  if ("action" in cliTools[args[0]].schema) {
29555
30046
  if (context.credentialScope !== "management" || !context.apiUrl || !context.apiToken) {
29556
30047
  console.error(`This command requires Management API context. Run \`${preferredCommand} status\` to inspect current detection.`);