@supacloud/cli 0.88.0 → 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/README.md CHANGED
@@ -284,9 +284,13 @@ supacloud --env production deploy --confirm-production production-ref
284
284
  Test and production use the same workflow. Production safety is enforced by the
285
285
  CLI confirmation gate; immutable releases, configuration revisions, backups,
286
286
  readiness, CAS activation, rollback artifacts and uncertain-result
287
- reconciliation belong to SupaCloud. The application layer only supplies its
288
- build output and configuration file; it does not implement backup, activation
289
- or downgrade orchestration.
287
+ reconciliation belong to SupaCloud. `SUPACLOUD_APPLICATION_CONFIGURATION_PATH`
288
+ and `SUPACLOUD_APPLICATION_CONFIGURATION_ID` are optional pins; when neither
289
+ is supplied, SupaCloud reuses the current configuration head for the selected
290
+ environment. A first deployment still requires a configuration revision to
291
+ exist on the platform. The application layer only supplies its build output and
292
+ optional configuration file; it does not implement backup, activation or
293
+ downgrade orchestration.
290
294
 
291
295
  CLI requests (including `status`) default to `SUPACLOUD_TLS_VERIFY=false`, skipping HTTPS certificate verification so that
292
296
  air-gapped installations using private or self-signed certificates work
@@ -632,41 +636,51 @@ reporting success. Management endpoint projection is checked before and after.
632
636
  ### Release scope rebinding and inspection
633
637
 
634
638
  Release scopes bind deployment manifests (functions, migrations, web assets) to
635
- an exact 40-character base commit SHA. When upstream `main` advances with
636
- unrelated fixes, `release scope_rebind` rebinds release manifests to the updated
637
- base commit without manual JSON editing or risk of dirtying non-manifest fields.
639
+ an exact 40-character base commit SHA and one concrete deployment environment.
640
+ Old scope files without `targetEnvironmentId` are rejected and must be
641
+ regenerated. Test and production scopes may share the same source commit, but
642
+ they are separate environment-bound release identities.
638
643
 
639
644
  ```bash
640
- # Rebind a single scope manifest to latest git main (or origin/main)
641
- supacloud release scope_rebind --file supacloud/fa/release-scopes/20260907-intake-p1-production.json
645
+ # Rebind a single environment-bound scope to latest git main (or origin/main)
646
+ supacloud release scope_rebind --file supacloud/fa/release-scopes/20260907-intake-p1-production.json --environment_id production
642
647
 
643
- # Rebind multiple files (e.g. production and staging together)
644
- supacloud release scope_rebind --files "20260907-intake-p1-production.json,20260907-intake-p1-staging.json"
648
+ # Rebind multiple production files together
649
+ supacloud release scope_rebind --files "20260907-intake-p1-production.json,20260907-intake-p1-production-hotfix.json" --environment_id production
650
+
651
+ # Rebind staging separately; a staging scope can never be rebound as production
652
+ supacloud release scope_rebind --task 20260907-intake-p1 --environment_id staging
645
653
 
646
654
  # Rebind all scopes matching a task ID or filename prefix in standard directories
647
- supacloud release scope_rebind --task 20260907-intake-p1
655
+ supacloud release scope_rebind --task 20260907-intake-p1 --environment_id production
648
656
 
649
657
  # Specify an explicit base commit SHA or git ref
650
- supacloud release scope_rebind --task 20260907-intake-p1 --base_commit origin/main
658
+ supacloud release scope_rebind --task 20260907-intake-p1 --base_commit origin/main --environment_id production
651
659
 
652
660
  # Preview changes without modifying files
653
- supacloud release scope_rebind --task 20260907-intake-p1 --dry_run
661
+ supacloud release scope_rebind --task 20260907-intake-p1 --environment_id production --dry_run
654
662
 
655
663
  # Inspect scope status, SHA-256 hash, and alignment with git HEAD / origin/main
656
- supacloud release scope_inspect --file 20260907-intake-p1-production.json
664
+ supacloud release scope_inspect --file 20260907-intake-p1-production.json --environment_id production
657
665
 
658
666
  # Scaffold a new canonical release scope
659
667
  supacloud release scope_create \
660
668
  --file supacloud/fa/release-scopes/20260907-intake-p1.json \
661
669
  --task FA-INTAKE-P1 \
662
670
  --ref abc123 \
671
+ --environment_id production \
663
672
  --functions "fa-api,fa-worker" \
664
673
  --migrations "20260907120000_init"
665
674
  ```
666
675
 
667
676
  Scope actions run locally without requiring Management API credentials. Files
668
677
  are formatted canonically with 2 spaces and sorted keys, and the authoritative
669
- `scope_sha256` checksum is recalculated automatically.
678
+ `scope_sha256` checksum is recalculated automatically. `scope_create` resolves
679
+ `origin/main` by default, so a new test or production scope is generated from
680
+ the current main baseline instead of reusing an older scope identity.
681
+ These commands are for release maintenance and audit workflows; customers do
682
+ not need to create or rebind a scope before running the one-command deployment
683
+ flow above.
670
684
 
671
685
  The legacy `.env` fallback is unclassified and therefore does not enable the
672
686
  production confirmation gate. Production automation must select a `prod` or
package/dist/index.js CHANGED
@@ -9167,7 +9167,7 @@ var ACTION_POLICY = {
9167
9167
  deploy: { write: ["deploy"] },
9168
9168
  applications: {
9169
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"],
9170
- 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"]
9171
9171
  },
9172
9172
  project: {
9173
9173
  read: ["get", "endpoints", "health", "logs", "api_keys", "settings", "tasks", "task_detail", "task_stats", "dlq", "background_settings"],
@@ -10493,34 +10493,67 @@ function parseApplicationDeployPlan(value) {
10493
10493
  throw new Error("Invalid application deploy plan action");
10494
10494
  return value;
10495
10495
  }
10496
- // node_modules/@supacloud/delivery/src/application-release-operation.ts
10496
+ // node_modules/@supacloud/delivery/src/application-deploy-operation.ts
10497
10497
  var closed3 = { additionalProperties: false };
10498
+ var sha256 = ApplicationReleaseIdSchema;
10498
10499
  var nullableActivation = Union([ApplicationActivationIdSchema, Null()]);
10499
- var ApplicationReleaseRequestSchema = _Object_({
10500
- 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,
10501
10510
  configuration_id: Optional(ApplicationConfigurationIdSchema),
10502
10511
  configuration_write: Optional(ApplicationConfigurationWriteSchema),
10503
- activation_id: Optional(ApplicationActivationIdSchema),
10504
- expected_activation_id: Optional(nullableActivation),
10505
- expected_plan_sha256: Optional(ApplicationReleaseIdSchema)
10512
+ expected_plan_sha256: Optional(sha256),
10513
+ approved_migration_digest: Optional(sha256),
10514
+ web: Optional(ApplicationDeployWebRequestSchema)
10506
10515
  }, closed3);
10507
- var ApplicationReleaseResultSchema = _Object_({
10508
- schema: Literal("supacloud.application-release.v1"),
10516
+ var ApplicationBackendReceiptSchema = _Object_({
10517
+ schema: Literal("supacloud.application-backend-receipt.v1"),
10509
10518
  project_ref: String2({ pattern: "^[a-z0-9-]{1,20}$" }),
10510
10519
  application_id: ApplicationIdSchema,
10511
10520
  environment_id: ApplicationIdSchema,
10512
- 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,
10513
10539
  configuration_id: ApplicationConfigurationIdSchema,
10514
10540
  activation_id: ApplicationActivationIdSchema,
10515
10541
  expected_activation_id: nullableActivation,
10516
- plan_sha256: ApplicationReleaseIdSchema,
10542
+ plan_sha256: sha256,
10517
10543
  action: Union([Literal("activate"), Literal("no-op")]),
10518
10544
  replayed: Boolean2(),
10519
- 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))
10520
10553
  }, closed3);
10521
- function parseApplicationReleaseResult(value) {
10522
- if (!Check2(ApplicationReleaseResultSchema, value)) {
10523
- throw new Error("Invalid application release result");
10554
+ function parseApplicationDeployResult(value) {
10555
+ if (!Check2(ApplicationDeployResultSchema, value)) {
10556
+ throw new Error("Invalid application deploy result");
10524
10557
  }
10525
10558
  return structuredClone(value);
10526
10559
  }
@@ -10721,7 +10754,7 @@ function parseApplicationReleaseTransferResult(candidate) {
10721
10754
  }
10722
10755
  // node_modules/@supacloud/delivery/src/deployment-evidence.ts
10723
10756
  var closed5 = { additionalProperties: false };
10724
- var sha256 = String2({ pattern: "^[a-f0-9]{64}$" });
10757
+ var sha2562 = String2({ pattern: "^[a-f0-9]{64}$" });
10725
10758
  var isoDate = String2({ minLength: 20, maxLength: 40 });
10726
10759
  var status = Union([
10727
10760
  Literal("confirmed"),
@@ -10741,7 +10774,7 @@ var DatabaseProviderSchema = _Object_({
10741
10774
  topology: Union([Literal("single-node"), Literal("distributed")]),
10742
10775
  migration: _Object_({
10743
10776
  status,
10744
- inventory_sha256: Union([sha256, Null()]),
10777
+ inventory_sha256: Union([sha2562, Null()]),
10745
10778
  compatibility: Union([
10746
10779
  Literal("verified"),
10747
10780
  Literal("not-proven"),
@@ -10789,9 +10822,9 @@ var DeploymentEvidenceSchema = _Object_({
10789
10822
  }, closed5),
10790
10823
  source: _Object_({
10791
10824
  commit_sha: Union([String2({ pattern: "^[0-9a-f]{7,64}$" }), Null()]),
10792
- manifest_sha256: sha256,
10825
+ manifest_sha256: sha2562,
10793
10826
  contract_schema: Union([String2({ minLength: 1, maxLength: 128 }), Null()]),
10794
- environment_binding_version: Union([sha256, Null()])
10827
+ environment_binding_version: Union([sha2562, Null()])
10795
10828
  }, closed5),
10796
10829
  database: DatabaseProviderSchema,
10797
10830
  components: _Array_(DeploymentComponentEvidenceSchema, { minItems: 1, maxItems: 32 }),
@@ -10847,9 +10880,9 @@ function parseDeploymentEvidence(value) {
10847
10880
  // node_modules/@supacloud/delivery/src/application-promotion.ts
10848
10881
  var closed6 = { additionalProperties: false };
10849
10882
  var projectRef2 = String2({ pattern: "^[a-z0-9-]{1,20}$" });
10850
- var sha2562 = ApplicationReleaseIdSchema;
10883
+ var sha2563 = ApplicationReleaseIdSchema;
10851
10884
  var nullableActivation2 = Union([ApplicationActivationIdSchema, Null()]);
10852
- var nullableDigest = Union([sha2562, Null()]);
10885
+ var nullableDigest = Union([sha2563, Null()]);
10853
10886
  var observation = {
10854
10887
  activation_id: nullableActivation2,
10855
10888
  receipt_confirmed: Boolean2(),
@@ -10875,24 +10908,24 @@ var ApplicationPromotionPlanSchema = _Object_({
10875
10908
  project_ref: projectRef2,
10876
10909
  application_id: ApplicationIdSchema,
10877
10910
  environment_id: ApplicationIdSchema,
10878
- manifest_sha256: sha2562,
10879
- plan_sha256: sha2562,
10911
+ manifest_sha256: sha2563,
10912
+ plan_sha256: sha2563,
10880
10913
  source: _Object_({
10881
10914
  project_ref: projectRef2,
10882
10915
  environment_id: ApplicationIdSchema,
10883
- release_id: sha2562,
10916
+ release_id: sha2563,
10884
10917
  ...observation
10885
10918
  }, closed6),
10886
10919
  target: _Object_({
10887
- candidate_release_id: sha2562,
10888
- current_release_id: Union([sha2562, Null()]),
10920
+ candidate_release_id: sha2563,
10921
+ current_release_id: Union([sha2563, Null()]),
10889
10922
  artifact_action: Union([Literal("materialize"), Literal("reuse")]),
10890
10923
  configuration_id: Union([ApplicationConfigurationIdSchema, Null()]),
10891
10924
  configuration: Union([ApplicationConfigurationViewSchema, Null()]),
10892
10925
  ...observation
10893
10926
  }, closed6),
10894
10927
  migrations: _Object_({
10895
- ledger_digest: sha2562,
10928
+ ledger_digest: sha2563,
10896
10929
  ledger_compatible: Boolean2(),
10897
10930
  project_migrations_applied: Boolean2(),
10898
10931
  pending_versions: _Array_(String2({ pattern: "^\\d{1,20}$" }), { maxItems: 4096, uniqueItems: true }),
@@ -16857,7 +16890,8 @@ var ApplicationActivationRetirementResultSchema2 = ApplicationActivationRetireme
16857
16890
  var ApplicationRollbackSnapshotSchema2 = ApplicationRollbackSnapshotSchema;
16858
16891
  var ApplicationActivationHistoryCursorSchema2 = ApplicationActivationHistoryCursorSchema;
16859
16892
  var ApplicationPromotionPlanSchema2 = ApplicationPromotionPlanSchema;
16860
- var ApplicationReleaseRequestSchema2 = ApplicationReleaseRequestSchema;
16893
+ var ApplicationDeployRequestSchema2 = ApplicationDeployRequestSchema;
16894
+ var ApplicationDeployWebRequestSchema2 = ApplicationDeployWebRequestSchema;
16861
16895
 
16862
16896
  // src/shared/mutation-protocol.ts
16863
16897
  import { createHash as createHash6 } from "node:crypto";
@@ -17645,7 +17679,7 @@ var APPLICATION_TOOL_SCHEMA = {
17645
17679
  "put_configuration",
17646
17680
  "get_deploy_plan",
17647
17681
  "deploy_release",
17648
- "release_application",
17682
+ "deploy_application",
17649
17683
  "get_release_transfer_plan",
17650
17684
  "transfer_release",
17651
17685
  "get_promotion_plan",
@@ -17673,14 +17707,15 @@ var APPLICATION_TOOL_SCHEMA = {
17673
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"),
17674
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"),
17675
17709
  configuration_path: optional(String2(), "[put_configuration] Local configuration write JSON including revision and expected revision"),
17676
- configuration_write: optional(Unknown(), "[release_application] Parsed immutable configuration write"),
17710
+ configuration_write: optional(Unknown(), "[deploy_application] Parsed immutable configuration write"),
17677
17711
  manifest_path: optional(String2(), "[upload_release] Local delivery.manifest.json"),
17712
+ web: optional(ApplicationDeployWebRequestSchema2, "[deploy_application] Immutable staged Web release binding"),
17678
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"),
17679
17714
  source_ref: optional(String2({ pattern: "^[A-Za-z0-9_-]{1,20}$" }), "[get_release_transfer_plan/transfer_release] Source project ref"),
17680
17715
  source_release_id: optional(ApplicationReleaseIdSchema2, "[get_release_transfer_plan/transfer_release] Source immutable release ID"),
17681
17716
  source_environment_id: optional(ApplicationIdSchema2, "[get_promotion_plan] Source environment"),
17682
- mutation_id: optional(ApplicationActivationIdSchema2, "[promote_application/get_promotion_status/reconcile_promotion] Stable promotion mutation ID"),
17683
- 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"),
17684
17719
  plan: optional(ApplicationPromotionPlanSchema2, "[promote_application] Immutable plan returned by get_promotion_plan"),
17685
17720
  cursor: optional(Union([ApplicationReleaseIdSchema2, ApplicationActivationHistoryCursorSchema2]), "[list_releases/get_history] Returned page cursor"),
17686
17721
  limit: optional(Integer({ minimum: 1, maximum: 100 }), "[list_releases/get_history] Page size, default 50/20"),
@@ -18033,57 +18068,50 @@ function registerApplicationTools(server, http) {
18033
18068
  const observed = await readDeployPlan(http, args, project);
18034
18069
  return observed.failure ?? releaseControlSuccess(operation, { ...observed.identity, plan: observed.plan });
18035
18070
  }
18036
- if (action === "release_application") {
18071
+ if (action === "deploy_application") {
18037
18072
  const environmentId = text(args, "environment_id");
18038
18073
  const releaseId = text(args, "release_id");
18039
- if (!Check2(ApplicationIdSchema2, environmentId) || !Check2(ApplicationReleaseIdSchema2, releaseId)) {
18040
- 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");
18041
18077
  }
18042
18078
  const configurationId = args["configuration_id"];
18043
- const activationId = args["activation_id"];
18044
- const expectedActivationId = args["expected_activation_id"];
18045
18079
  if (configurationId !== undefined && !Check2(ApplicationConfigurationIdSchema2, configurationId)) {
18046
18080
  throw new Error("Invalid application configuration ID");
18047
18081
  }
18048
- if (activationId !== undefined && !Check2(ApplicationActivationIdSchema2, activationId)) {
18049
- throw new Error("Invalid application activation ID");
18050
- }
18051
- if (expectedActivationId !== undefined && expectedActivationId !== null && expectedActivationId !== "absent" && !Check2(ApplicationActivationIdSchema2, expectedActivationId)) {
18052
- throw new Error("Invalid expected activation ID");
18053
- }
18054
18082
  const configurationWrite = args["configuration_write"];
18055
18083
  const parsedConfigurationWrite = configurationWrite === undefined ? undefined : parseApplicationConfigurationWrite(configurationWrite);
18056
18084
  const requestedConfigurationId = configurationId ?? parsedConfigurationWrite?.configuration_id;
18057
18085
  const body = {
18086
+ mutation_id: mutationId,
18058
18087
  release_id: releaseId,
18059
18088
  ...configurationId === undefined ? {} : { configuration_id: configurationId },
18060
18089
  ...configurationWrite === undefined ? {} : { configuration_write: configurationWrite },
18061
- ...activationId === undefined ? {} : { activation_id: activationId },
18062
- ...expectedActivationId === undefined ? {} : {
18063
- expected_activation_id: expectedActivationId === "absent" ? null : expectedActivationId
18064
- }
18090
+ ...typeof args["approved_migration_digest"] === "string" ? { approved_migration_digest: args["approved_migration_digest"] } : {},
18091
+ ...args["web"] === undefined ? {} : { web: args["web"] }
18065
18092
  };
18066
- if (!Check2(ApplicationReleaseRequestSchema2, body)) {
18067
- throw new Error("Invalid application release request");
18093
+ if (!Check2(ApplicationDeployRequestSchema2, body)) {
18094
+ throw new Error("Invalid application deploy request");
18068
18095
  }
18069
- 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 });
18070
18097
  const identity = {
18071
18098
  project_ref: ref,
18072
18099
  application_id: id,
18073
18100
  environment_id: environmentId,
18074
18101
  release_id: releaseId,
18075
- ...configurationId === undefined ? {} : { configuration_id: configurationId },
18076
- ...activationId === undefined ? {} : { activation_id: activationId }
18102
+ mutation_id: mutationId,
18103
+ ...requestedConfigurationId === undefined ? {} : { configuration_id: requestedConfigurationId }
18077
18104
  };
18078
18105
  if (!result.ok)
18079
18106
  return releaseControlMutationFailure(operation, result, identity);
18080
18107
  try {
18081
18108
  if (result.status !== 200)
18082
18109
  throw new Error;
18083
- const release = parseApplicationReleaseResult(result.data);
18084
- 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) {
18085
18112
  throw new Error;
18086
- return releaseControlSuccess(operation, release);
18113
+ }
18114
+ return releaseControlSuccess(operation, deploy);
18087
18115
  } catch {
18088
18116
  return releaseControlFailure(operation, "OUTCOME_UNKNOWN", result.status, identity);
18089
18117
  }
@@ -24538,6 +24566,7 @@ var REMOTE_APP_DESCRIPTIONS = {
24538
24566
  source_environment_id: "[promote-plan/diff/promote] Source environment ID",
24539
24567
  mutation_id: "[promote-status/promote-reconcile] Stable promotion mutation ID",
24540
24568
  approved_migration_digest: "[promote] Exact reviewed migration execution-plan digest",
24569
+ web: "[release] Immutable staged Web release binding prepared by the deploy profile",
24541
24570
  branch_ref: "[preview-plan] Preview branch identity",
24542
24571
  branch_name: "[preview/preview-create] Human-readable preview branch name",
24543
24572
  data_mode: "[preview-plan/preview/preview-create] schema_only or full_clone",
@@ -25506,9 +25535,6 @@ async function runAppRelease(request, options, delegate) {
25506
25535
  if (request.configuration_path !== undefined && request.configuration_id !== undefined) {
25507
25536
  throw new Error("app release accepts either --configuration_path or --configuration_id, not both");
25508
25537
  }
25509
- if (request.configuration_path === undefined && request.configuration_id === undefined) {
25510
- throw new Error("app release requires --configuration_path or --configuration_id");
25511
- }
25512
25538
  if (request.activation_id !== undefined || request.expected_activation_id !== undefined) {
25513
25539
  throw new Error("app release generates one activation identity; use app deploy for an exact retry");
25514
25540
  }
@@ -25562,25 +25588,36 @@ async function runAppRelease(request, options, delegate) {
25562
25588
  configurationWrite = parseApplicationConfigurationWrite(await Bun.file(configurationPath).json());
25563
25589
  configurationId = configurationWrite.configuration_id;
25564
25590
  } catch {
25565
- 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);
25566
25592
  }
25567
25593
  completed.configure = { delegated: true, configuration_id: configurationId };
25568
25594
  } else {
25569
- completed.configure = { delegated: true, configuration_id: configurationId };
25595
+ completed.configure = {
25596
+ delegated: false,
25597
+ resolved_by: "platform",
25598
+ ...configurationId === undefined ? {} : { configuration_id: configurationId }
25599
+ };
25570
25600
  }
25571
25601
  const deployArgs = {
25572
- action: "release_application",
25602
+ action: "deploy_application",
25573
25603
  ref,
25574
25604
  id,
25575
25605
  environment_id: environmentId,
25576
25606
  release_id: releaseId,
25577
- configuration_id: configurationId,
25578
- activation_id: activationId,
25579
- ...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 }
25580
25611
  };
25581
25612
  validateToolArguments(APPLICATION_TOOL_SCHEMA, deployArgs);
25582
25613
  const deployed = await delegate(deployArgs);
25583
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
+ }
25584
25621
  if (deployed.isError || !deployReceipt?.ok) {
25585
25622
  return releasePhaseFailure("deploy", deployed, completed);
25586
25623
  }
@@ -25593,13 +25630,14 @@ async function runAppRelease(request, options, delegate) {
25593
25630
  application_id: id,
25594
25631
  environment_id: environmentId,
25595
25632
  release_id: releaseId,
25596
- configuration_id: configurationId,
25633
+ ...configurationId === undefined ? {} : { configuration_id: configurationId },
25597
25634
  manifest_path: manifestPath,
25598
25635
  build: buildReceipt ? completed.build : { skipped: true, manifest_path: manifestPath },
25599
25636
  phases: completed,
25600
25637
  no_op: deployReceipt.action === "no-op" || deployReceipt.no_op === true,
25601
25638
  execution_performed: deployReceipt.execution_performed === true,
25602
- ...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 }
25603
25641
  };
25604
25642
  return request.format === "json" || request.json === true ? textResult(JSON.stringify(result, null, 2)) : textResult([
25605
25643
  `${ref}/${id}/${environmentId}: ${result.no_op ? "no-op" : "released"}`,
@@ -27205,6 +27243,7 @@ import { createHash as createHash8 } from "node:crypto";
27205
27243
  import { existsSync as existsSync13, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "node:fs";
27206
27244
  import { dirname as dirname10, join as join16, relative as relative9, resolve as resolve21 } from "node:path";
27207
27245
  var SAFE_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
27246
+ var SAFE_ENVIRONMENT_ID = /^[A-Za-z0-9_-]{1,64}$/;
27208
27247
  var BACKUP_ID = /^logical-full_[A-Za-z0-9_-]{1,64}_[a-f0-9]{32}$/;
27209
27248
  var SHA256 = /^[a-f0-9]{64}$/;
27210
27249
  var SAFE_DATABASE = /^[^\u0000-\u001f\u007f]{1,128}$/;
@@ -27445,6 +27484,27 @@ function genericScopedReleaseScopeJson(scope) {
27445
27484
  function genericScopedReleaseScopeSha256(scope) {
27446
27485
  return createHash8("sha256").update(genericScopedReleaseScopeJson(scope)).digest("hex");
27447
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
+ }
27448
27508
  function runGitRevParse(cwd, args) {
27449
27509
  const res = Bun.spawnSync(["git", ...args], { cwd });
27450
27510
  if (res.exitCode !== 0 || !res.stdout)
@@ -27571,10 +27631,10 @@ function collectScopeFiles(cwd, file, files, dir, task) {
27571
27631
  function handleScopeRebind(args) {
27572
27632
  const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
27573
27633
  const targetBaseCommit = resolveGitBaseCommit(cwd, args.base_commit);
27634
+ const requestedEnvironmentId = requestedScopeEnvironment(args, "scope_rebind");
27574
27635
  const scopeFiles = collectScopeFiles(cwd, args.file, args.files, args.dir, args.task);
27575
27636
  const dryRun = args.dry_run === true;
27576
- const scopes = [];
27577
- for (const filePath of scopeFiles) {
27637
+ const parsedScopes = scopeFiles.map((filePath) => {
27578
27638
  let content;
27579
27639
  try {
27580
27640
  content = readFileSync10(filePath, "utf8");
@@ -27587,15 +27647,20 @@ function handleScopeRebind(args) {
27587
27647
  } catch {
27588
27648
  throw new Error(`Scope file '${filePath}' is not valid JSON`);
27589
27649
  }
27590
- 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)) {
27591
27653
  throw new Error(`Scope file '${filePath}' has missing or invalid baseCommit`);
27592
27654
  }
27655
+ return { filePath, parsed, targetEnvironmentId };
27656
+ });
27657
+ const scopes = [];
27658
+ for (const { filePath, parsed, targetEnvironmentId } of parsedScopes) {
27593
27659
  const previousBaseCommit = parsed.baseCommit;
27594
27660
  const updated = previousBaseCommit !== targetBaseCommit;
27595
27661
  if (updated) {
27596
27662
  parsed.baseCommit = targetBaseCommit;
27597
27663
  }
27598
- const canonicalJson = genericScopedReleaseScopeJson(parsed);
27599
27664
  const scopeSha256 = genericScopedReleaseScopeSha256(parsed);
27600
27665
  if (updated && !dryRun) {
27601
27666
  const formatted = JSON.stringify(canonicalValue(parsed), null, 2) + `
@@ -27608,6 +27673,7 @@ function handleScopeRebind(args) {
27608
27673
  target_project_ref: typeof parsed.targetProjectRef === "string" ? parsed.targetProjectRef : "",
27609
27674
  previous_base_commit: previousBaseCommit,
27610
27675
  new_base_commit: targetBaseCommit,
27676
+ target_environment_id: targetEnvironmentId,
27611
27677
  scope_sha256: scopeSha256,
27612
27678
  updated
27613
27679
  });
@@ -27622,6 +27688,7 @@ function handleScopeRebind(args) {
27622
27688
  }
27623
27689
  function handleScopeInspect(args) {
27624
27690
  const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
27691
+ const requestedEnvironmentId = requestedScopeEnvironment(args, "scope_inspect");
27625
27692
  if (typeof args.file !== "string" || !args.file.trim()) {
27626
27693
  throw new Error("'file' is required for scope_inspect");
27627
27694
  }
@@ -27651,7 +27718,11 @@ function handleScopeInspect(args) {
27651
27718
  } catch {
27652
27719
  throw new Error(`Scope file '${args.file}' is not valid JSON`);
27653
27720
  }
27654
- 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)) {
27655
27726
  throw new Error(`Scope file '${args.file}' has missing or invalid baseCommit`);
27656
27727
  }
27657
27728
  const scopeSha256 = genericScopedReleaseScopeSha256(parsed);
@@ -27661,6 +27732,7 @@ function handleScopeInspect(args) {
27661
27732
  scope: {
27662
27733
  task_id: parsed.taskId,
27663
27734
  target_project_ref: parsed.targetProjectRef,
27735
+ target_environment_id: targetEnvironmentId,
27664
27736
  base_commit: parsed.baseCommit,
27665
27737
  functions: parsed.functions,
27666
27738
  excluded_functions: parsed.excludedFunctions,
@@ -27698,6 +27770,7 @@ function handleScopeCreate(args, fallbackRef) {
27698
27770
  if (typeof args.task !== "string" || !args.task.trim()) {
27699
27771
  throw new Error("'task' is required for scope_create");
27700
27772
  }
27773
+ const targetEnvironmentId = requestedScopeEnvironment(args, "scope_create");
27701
27774
  const projectRef = typeof args.ref === "string" && args.ref.trim() || fallbackRef;
27702
27775
  if (!projectRef) {
27703
27776
  throw new Error("'ref' is required for scope_create");
@@ -27714,6 +27787,7 @@ function handleScopeCreate(args, fallbackRef) {
27714
27787
  functions: parseCommaList(args.functions),
27715
27788
  migrations: parseCommaList(args.migrations),
27716
27789
  targetProjectRef: projectRef,
27790
+ targetEnvironmentId,
27717
27791
  taskId: String(args.task).trim(),
27718
27792
  web: Boolean(args.web)
27719
27793
  };
@@ -27726,6 +27800,7 @@ function handleScopeCreate(args, fallbackRef) {
27726
27800
  file: relative9(cwd, filePath),
27727
27801
  task_id: scope.taskId,
27728
27802
  target_project_ref: projectRef,
27803
+ target_environment_id: targetEnvironmentId,
27729
27804
  base_commit: targetBaseCommit,
27730
27805
  scope_sha256: scopeSha256,
27731
27806
  scope
@@ -27752,6 +27827,7 @@ function registerReleaseTools(server, http, options = {}) {
27752
27827
  dir: optional(String2(), "[scope_rebind] Directory containing release scope JSON files (defaults to supacloud/fa/release-scopes, release-scopes, or scopes)"),
27753
27828
  task: optional(String2(), "[scope_rebind/scope_create] Task ID or prefix filter for scope files"),
27754
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"),
27755
27831
  functions: optional(String2(), "[scope_create] Comma-separated list of functions to deploy"),
27756
27832
  excluded_functions: optional(String2(), "[scope_create] Comma-separated list of functions to exclude"),
27757
27833
  migrations: optional(String2(), "[scope_create] Comma-separated list of migrations to apply"),
@@ -28153,6 +28229,33 @@ function selectDeployment(deployments, requestedId, projectName) {
28153
28229
  const ids = deployments.map((deployment) => deployment.id).sort().join(", ");
28154
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`);
28155
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
+ }
28156
28259
  function targetRoot(configRoot, target, name) {
28157
28260
  const root = resolve22(configRoot, target.root || (name === "frontend" ? "." : name));
28158
28261
  const prefix = `${resolve22(configRoot)}${sep5}`;
@@ -28325,6 +28428,71 @@ function payload(response) {
28325
28428
  throw new Error("Deployment operation returned an invalid response");
28326
28429
  return parsed;
28327
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
+ }
28328
28496
  function phaseReporter(json) {
28329
28497
  const started = performance.now();
28330
28498
  return (phase, detail) => {
@@ -28373,12 +28541,10 @@ function registerDeployTools(server, http, options = {}) {
28373
28541
  }
28374
28542
  const configurationPath = options.application.configurationPath?.trim();
28375
28543
  const configurationId = options.application.configurationId?.trim();
28376
- if (!configurationPath && !configurationId) {
28377
- throw new Error("Application profile requires SUPACLOUD_APPLICATION_CONFIGURATION_PATH or SUPACLOUD_APPLICATION_CONFIGURATION_ID");
28378
- }
28379
28544
  if (configurationPath && configurationId) {
28380
28545
  throw new Error("Application profile must set only one of SUPACLOUD_APPLICATION_CONFIGURATION_PATH or SUPACLOUD_APPLICATION_CONFIGURATION_ID");
28381
28546
  }
28547
+ const stagedWeb = await stageApplicationWebRelease(http, projectRef, configRoot, args.skip_build === true, args.json === true, options.environmentVariables);
28382
28548
  const result = await options.applicationRelease({
28383
28549
  action: "release",
28384
28550
  ref: projectRef,
@@ -28389,6 +28555,7 @@ function registerDeployTools(server, http, options = {}) {
28389
28555
  ...configurationId ? { configuration_id: configurationId } : {},
28390
28556
  ...options.application.manifestPath ? { manifest_path: options.application.manifestPath } : {},
28391
28557
  ...args.skip_build === true ? { skip_build: true } : {},
28558
+ ...stagedWeb?.request === undefined ? {} : { web: stagedWeb.request },
28392
28559
  ...args.json === true ? { format: "json", json: true } : {}
28393
28560
  });
28394
28561
  return result;
@@ -29205,7 +29372,7 @@ function registerDeveloperExperienceTools(server, options = {}) {
29205
29372
  // package.json
29206
29373
  var package_default = {
29207
29374
  name: "@supacloud/cli",
29208
- version: "0.88.0",
29375
+ version: "0.90.0",
29209
29376
  description: "Project-scoped CLI for SupaCloud users",
29210
29377
  type: "module",
29211
29378
  main: "./dist/index.js",
@@ -29258,13 +29425,13 @@ var package_default = {
29258
29425
  },
29259
29426
  dependencies: {
29260
29427
  "@supacloud/compiler": "^0.44.1",
29261
- "@supacloud/delivery": "0.14.1",
29428
+ "@supacloud/delivery": "0.15.0",
29262
29429
  "@supacloud/db": "0.16.1",
29263
29430
  typebox: "1.3.36"
29264
29431
  },
29265
29432
  overrides: {
29266
29433
  "@supacloud/contracts": "0.8.1",
29267
- "@supacloud/delivery": "0.14.1",
29434
+ "@supacloud/delivery": "0.15.0",
29268
29435
  "@graphql-tools/utils": "^12.0.3"
29269
29436
  },
29270
29437
  devDependencies: {
@@ -29498,8 +29665,8 @@ EXAMPLES
29498
29665
  ${preferredCommand} release postgrest_restart --ref abc123
29499
29666
  ${preferredCommand} release release_canary_fixture_stage_replay --ref abc123 --subject <uuid> --request_id <uuid>
29500
29667
  ${preferredCommand} release release_canary_fixture_disable_replay --ref abc123 --fixture_id <uuid> --disable_request_id <uuid> --issuer <issuer-url> --subject <uuid>
29501
- ${preferredCommand} release scope_rebind --file supacloud/fa/release-scopes/20260907-intake-p1-production.json
29502
- ${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
29503
29670
  ${preferredCommand} queue stats --queue emails
29504
29671
  ${preferredCommand} queue dlq --queue emails --limit 20
29505
29672
  ${preferredCommand} frontend list --ref abc123
@@ -1,3 +1,4 @@
1
+ import type { ApplicationDeployRequest } from "@supacloud/delivery";
1
2
  import { type ToolServer } from "../tool-server";
2
3
  import { type AppManifest } from "./app-tool-export";
3
4
  declare const REMOTE_APP_ACTIONS: {
@@ -53,6 +54,7 @@ export interface AppToolArguments {
53
54
  source_environment_id?: string;
54
55
  mutation_id?: string;
55
56
  approved_migration_digest?: string;
57
+ web?: ApplicationDeployRequest["web"];
56
58
  branch_ref?: string;
57
59
  branch_name?: string;
58
60
  data_mode?: "schema_only" | "full_clone";
@@ -152,7 +152,8 @@ export declare const ApplicationPromotionPlanSchema: import("typebox").TObject<{
152
152
  execution_performed: import("typebox").TLiteral<false>;
153
153
  data_recovery: import("typebox").TLiteral<"separate-required">;
154
154
  }>;
155
- export declare const ApplicationReleaseRequestSchema: import("typebox").TObject<{
155
+ export declare const ApplicationDeployRequestSchema: import("typebox").TObject<{
156
+ mutation_id: import("typebox").TString;
156
157
  release_id: import("typebox").TString;
157
158
  configuration_id: import("typebox").TOptional<import("typebox").TString>;
158
159
  configuration_write: import("typebox").TOptional<import("typebox").TObject<{
@@ -168,15 +169,22 @@ export declare const ApplicationReleaseRequestSchema: import("typebox").TObject<
168
169
  }>>;
169
170
  }>;
170
171
  }>>;
171
- activation_id: import("typebox").TOptional<import("typebox").TString>;
172
- expected_activation_id: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TString, import("typebox").TNull]>>;
173
172
  expected_plan_sha256: import("typebox").TOptional<import("typebox").TString>;
173
+ approved_migration_digest: import("typebox").TOptional<import("typebox").TString>;
174
+ web: import("typebox").TOptional<import("typebox").TObject<{
175
+ deployment_id: import("typebox").TString;
176
+ release_id: import("typebox").TString;
177
+ expected_active_release_id: import("typebox").TString;
178
+ expected_activation_id: import("typebox").TString;
179
+ mutation_id: import("typebox").TString;
180
+ }>>;
174
181
  }>;
175
- export declare const ApplicationReleaseResultSchema: import("typebox").TObject<{
176
- schema: import("typebox").TLiteral<"supacloud.application-release.v1">;
182
+ export declare const ApplicationDeployResultSchema: import("typebox").TObject<{
183
+ schema: import("typebox").TLiteral<"supacloud.application-deploy.v1">;
177
184
  project_ref: import("typebox").TString;
178
185
  application_id: import("typebox").TString;
179
186
  environment_id: import("typebox").TString;
187
+ mutation_id: import("typebox").TString;
180
188
  release_id: import("typebox").TString;
181
189
  configuration_id: import("typebox").TString;
182
190
  activation_id: import("typebox").TString;
@@ -185,4 +193,33 @@ export declare const ApplicationReleaseResultSchema: import("typebox").TObject<{
185
193
  action: import("typebox").TUnion<[import("typebox").TLiteral<"activate">, import("typebox").TLiteral<"no-op">]>;
186
194
  replayed: import("typebox").TBoolean;
187
195
  execution_performed: import("typebox").TBoolean;
196
+ backend_receipt: import("typebox").TObject<{
197
+ schema: import("typebox").TLiteral<"supacloud.application-backend-receipt.v1">;
198
+ project_ref: import("typebox").TString;
199
+ application_id: import("typebox").TString;
200
+ environment_id: import("typebox").TString;
201
+ release_id: import("typebox").TString;
202
+ manifest_sha256: import("typebox").TString;
203
+ mutation_id: import("typebox").TString;
204
+ plan_sha256: import("typebox").TString;
205
+ backup_id: import("typebox").TUnion<[import("typebox").TString, import("typebox").TNull]>;
206
+ before_ledger_digest: import("typebox").TString;
207
+ after_ledger_digest: import("typebox").TString;
208
+ applied_versions: import("typebox").TArray<import("typebox").TString>;
209
+ schema_reloaded: import("typebox").TBoolean;
210
+ postgrest_healthy: import("typebox").TBoolean;
211
+ }>;
212
+ web: import("typebox").TOptional<import("typebox").TObject<{
213
+ deployment_id: import("typebox").TString;
214
+ release_id: import("typebox").TString;
215
+ activation_id: import("typebox").TString;
216
+ replayed: import("typebox").TBoolean;
217
+ }>>;
218
+ }>;
219
+ export declare const ApplicationDeployWebRequestSchema: import("typebox").TObject<{
220
+ deployment_id: import("typebox").TString;
221
+ release_id: import("typebox").TString;
222
+ expected_active_release_id: import("typebox").TString;
223
+ expected_activation_id: import("typebox").TString;
224
+ mutation_id: import("typebox").TString;
188
225
  }>;
@@ -9,7 +9,7 @@ export declare const APPLICATION_TOOL_SCHEMA: {
9
9
  ttl_seconds: Type.TOptional<Type.TInteger>;
10
10
  wait: Type.TOptional<Type.TBoolean>;
11
11
  timeout_seconds: Type.TOptional<Type.TInteger>;
12
- action: Type.TEnum<("list_releases" | "get_release" | "upload_release" | "activate_release" | "logs" | "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" | "get_preview_plan" | "list_previews" | "get_preview" | "put_configuration" | "release_application" | "transfer_release" | "promote_application" | "reconcile_promotion" | "create_preview" | "reconcile_preview" | "cleanup_preview" | "deploy_release" | "rollback_release" | "reconcile_activation" | "retire_activation")[]>;
12
+ action: Type.TEnum<("list_releases" | "get_release" | "upload_release" | "activate_release" | "logs" | "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" | "get_preview_plan" | "list_previews" | "get_preview" | "put_configuration" | "deploy_application" | "transfer_release" | "promote_application" | "reconcile_promotion" | "create_preview" | "reconcile_preview" | "cleanup_preview" | "deploy_release" | "rollback_release" | "reconcile_activation" | "retire_activation")[]>;
13
13
  ref: Type.TString;
14
14
  id: Type.TString;
15
15
  environment_id: Type.TOptional<Type.TString>;
@@ -19,6 +19,13 @@ export declare const APPLICATION_TOOL_SCHEMA: {
19
19
  configuration_path: Type.TOptional<Type.TString>;
20
20
  configuration_write: Type.TOptional<Type.TUnknown>;
21
21
  manifest_path: Type.TOptional<Type.TString>;
22
+ web: Type.TOptional<Type.TObject<{
23
+ deployment_id: Type.TString;
24
+ release_id: Type.TString;
25
+ expected_active_release_id: Type.TString;
26
+ expected_activation_id: Type.TString;
27
+ mutation_id: Type.TString;
28
+ }>>;
22
29
  release_id: Type.TOptional<Type.TString>;
23
30
  source_ref: Type.TOptional<Type.TString>;
24
31
  source_release_id: Type.TOptional<Type.TString>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.88.0",
3
+ "version": "0.90.0",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,13 +53,13 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "@supacloud/compiler": "^0.44.1",
56
- "@supacloud/delivery": "0.14.1",
56
+ "@supacloud/delivery": "0.15.0",
57
57
  "@supacloud/db": "0.16.1",
58
58
  "typebox": "1.3.36"
59
59
  },
60
60
  "overrides": {
61
61
  "@supacloud/contracts": "0.8.1",
62
- "@supacloud/delivery": "0.14.1",
62
+ "@supacloud/delivery": "0.15.0",
63
63
  "@graphql-tools/utils": "^12.0.3"
64
64
  },
65
65
  "devDependencies": {
@@ -23,9 +23,9 @@ Load this reference when selecting a command surface or when a user asks an AI t
23
23
  | Inspect or back up a remote database | `supabase db_pull`, `migration_list`, `db_dump`, `gen_types` | Requires explicit PostgreSQL DSN; redact it |
24
24
  | Replay the release-canary fixture stage receipt | `release release_canary_fixture_stage_replay` | Dual-bind Management project context and that project's service-role application origin; accepts only the exact subject/request UUID pair and a strict idempotent receipt |
25
25
  | Disable a staged release-canary fixture | `release release_canary_fixture_disable_replay` | Uses the fixed disable RPC once, accepts idempotent false/true receipts, then queries the existing pending RPC with issuer/subject and requires authoritative JSON `false`; fixture binding comes from the strict disable receipt |
26
- | Rebind release scope manifest to upstream base commit | `release scope_rebind` | Local action; updates baseCommit in scope JSON files canonically to target git commit, supports `--dry_run`, `--task`, and recalculates `scope_sha256` |
27
- | Inspect release scope manifest and git alignment | `release scope_inspect` | Local action; validates scope structure, returns canonical hash, and checks alignment with HEAD / origin/main |
28
- | Scaffold a new canonical release scope manifest | `release scope_create` | Local action; outputs canonical JSON scope with sorted functions, migrations, and baseCommit |
26
+ | Rebind release scope manifest to upstream base commit | `release scope_rebind` | Local maintenance action; requires the exact `--environment_id`, updates only scopes bound to that environment, supports `--dry_run`/`--task`, and recalculates `scope_sha256` |
27
+ | Inspect release scope manifest and git alignment | `release scope_inspect` | Local maintenance action; requires the exact `--environment_id`, rejects legacy or cross-environment scopes, returns canonical hash, and checks alignment with HEAD / origin/main |
28
+ | Scaffold a new canonical release scope manifest | `release scope_create` | Local maintenance action; requires `--environment_id` and outputs a current-main-bound scope with sorted functions, migrations, and environment identity |
29
29
  | Preview/apply migrations remotely | `supabase push` | Always dry-run first; production needs explicit approval |
30
30
  | Mark proven-equivalent historical migrations as applied | `database baseline_migrations` | Dry-run, schema-equivalence proof, backup, explicit approval |
31
31
  | Inspect auth users or generate a controlled login link | `auth list_users`, `auth get_user`, `auth generate_link` | User reads are bounded; generation supports only `magiclink`, `recovery`, and `invite`, requires production confirmation, and returns only a validated action URL |
@@ -51,7 +51,7 @@ until a project-scoped context is resolved.
51
51
  - `secrets`: project secret management; never print values after write.
52
52
  - `queue`, `task_events`, `diagnostics`: asynchronous workload operations and bounded diagnostics.
53
53
  - `gateway`: project route/config/rebuild operations.
54
- - `release`: local release scope rebinding/inspection (`scope_rebind`, `scope_inspect`, `scope_create`), verified backup/PostgREST lifecycle controls, plus production-confirmed, non-retried, strict release-canary fixture stage and disable replay actions.
54
+ - `release`: local release scope maintenance (`scope_rebind`, `scope_inspect`, `scope_create`), verified backup/PostgREST lifecycle controls, plus production-confirmed, non-retried, strict release-canary fixture stage and disable replay actions. Scope maintenance is not a prerequisite for the normal `supacloud --env <name> deploy` flow.
55
55
  - `ai`: inspect or install this packaged Skill.
56
56
 
57
57
  ## Safe inspection pattern