@supacloud/cli 0.88.0 → 0.90.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -37
- package/dist/index.js +277 -103
- package/dist/shared/schema.d.ts +1 -1
- package/dist/shared/tools/app-tools.d.ts +2 -0
- package/dist/shared/tools/application-schemas.d.ts +42 -5
- package/dist/shared/tools/application-tools.d.ts +8 -1
- package/dist/tool-runtime.js +2 -0
- package/package.json +3 -3
- package/skills/supacloud/references/command-map.md +4 -4
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.
|
|
288
|
-
|
|
289
|
-
|
|
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
|
|
@@ -629,44 +633,25 @@ query parameters and accepts only its authoritative JSON boolean `false`
|
|
|
629
633
|
read-back. The fixture binding comes from the validated disable receipt before
|
|
630
634
|
reporting success. Management endpoint projection is checked before and after.
|
|
631
635
|
|
|
632
|
-
###
|
|
636
|
+
### Internal release scope controls
|
|
633
637
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
base commit without manual JSON editing or risk of dirtying non-manifest fields.
|
|
638
|
+
Scope files and manual release-controller operations are internal compatibility
|
|
639
|
+
surfaces. They are not part of the normal customer deployment path. Use the
|
|
640
|
+
project command instead:
|
|
638
641
|
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
supacloud
|
|
642
|
-
|
|
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"
|
|
645
|
-
|
|
646
|
-
# Rebind all scopes matching a task ID or filename prefix in standard directories
|
|
647
|
-
supacloud release scope_rebind --task 20260907-intake-p1
|
|
648
|
-
|
|
649
|
-
# Specify an explicit base commit SHA or git ref
|
|
650
|
-
supacloud release scope_rebind --task 20260907-intake-p1 --base_commit origin/main
|
|
651
|
-
|
|
652
|
-
# Preview changes without modifying files
|
|
653
|
-
supacloud release scope_rebind --task 20260907-intake-p1 --dry_run
|
|
654
|
-
|
|
655
|
-
# Inspect scope status, SHA-256 hash, and alignment with git HEAD / origin/main
|
|
656
|
-
supacloud release scope_inspect --file 20260907-intake-p1-production.json
|
|
657
|
-
|
|
658
|
-
# Scaffold a new canonical release scope
|
|
659
|
-
supacloud release scope_create \
|
|
660
|
-
--file supacloud/fa/release-scopes/20260907-intake-p1.json \
|
|
661
|
-
--task FA-INTAKE-P1 \
|
|
662
|
-
--ref abc123 \
|
|
663
|
-
--functions "fa-api,fa-worker" \
|
|
664
|
-
--migrations "20260907120000_init"
|
|
642
|
+
```sh
|
|
643
|
+
supacloud --env test deploy
|
|
644
|
+
supacloud --env production deploy --confirm-production PROJECT_REF
|
|
665
645
|
```
|
|
666
646
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
`
|
|
647
|
+
FA deployment policy and the boundary between platform-owned operations and
|
|
648
|
+
FA-owned business features are documented in
|
|
649
|
+
[`docs/fa-deployment-capability-boundary.md`](../../docs/fa-deployment-capability-boundary.md).
|
|
650
|
+
|
|
651
|
+
Scope creation, rebinding, inspection and hash calculation are intentionally
|
|
652
|
+
undocumented here. They remain available only for internal compatibility,
|
|
653
|
+
audit and historical receipt migration. Customers do not need a scope file
|
|
654
|
+
before running the one-command deployment flow above.
|
|
670
655
|
|
|
671
656
|
The legacy `.env` fallback is unclassified and therefore does not enable the
|
|
672
657
|
production confirmation gate. Production automation must select a `prod` or
|
package/dist/index.js
CHANGED
|
@@ -8713,6 +8713,8 @@ function decodedSchema(inputSchema, outputSchema, decode, options) {
|
|
|
8713
8713
|
return result;
|
|
8714
8714
|
}
|
|
8715
8715
|
function schemaEnumValues(schema) {
|
|
8716
|
+
if (!schema)
|
|
8717
|
+
return [];
|
|
8716
8718
|
const view = schema;
|
|
8717
8719
|
if (Array.isArray(view.enum)) {
|
|
8718
8720
|
return view.enum.filter((value) => typeof value === "string" || typeof value === "number").map((value) => String(value));
|
|
@@ -8818,7 +8820,7 @@ async function runCli(cliTools, args, options = {}) {
|
|
|
8818
8820
|
`);
|
|
8819
8821
|
const getSchemaShape = (schema) => schemaProperties(schema);
|
|
8820
8822
|
const getEnumOptions = (field) => schemaEnumValues(field);
|
|
8821
|
-
const getDescription = (field) => schemaDescription(field);
|
|
8823
|
+
const getDescription = (field) => field ? schemaDescription(field) : "";
|
|
8822
8824
|
const formatToolHelp = (toolName, tool) => {
|
|
8823
8825
|
const shape = getSchemaShape(tool.schema);
|
|
8824
8826
|
const actionField = shape.action;
|
|
@@ -9167,7 +9169,7 @@ var ACTION_POLICY = {
|
|
|
9167
9169
|
deploy: { write: ["deploy"] },
|
|
9168
9170
|
applications: {
|
|
9169
9171
|
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", "
|
|
9172
|
+
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
9173
|
},
|
|
9172
9174
|
project: {
|
|
9173
9175
|
read: ["get", "endpoints", "health", "logs", "api_keys", "settings", "tasks", "task_detail", "task_stats", "dlq", "background_settings"],
|
|
@@ -10493,34 +10495,67 @@ function parseApplicationDeployPlan(value) {
|
|
|
10493
10495
|
throw new Error("Invalid application deploy plan action");
|
|
10494
10496
|
return value;
|
|
10495
10497
|
}
|
|
10496
|
-
// node_modules/@supacloud/delivery/src/application-
|
|
10498
|
+
// node_modules/@supacloud/delivery/src/application-deploy-operation.ts
|
|
10497
10499
|
var closed3 = { additionalProperties: false };
|
|
10500
|
+
var sha256 = ApplicationReleaseIdSchema;
|
|
10498
10501
|
var nullableActivation = Union([ApplicationActivationIdSchema, Null()]);
|
|
10499
|
-
var
|
|
10500
|
-
|
|
10502
|
+
var ApplicationDeployWebRequestSchema = _Object_({
|
|
10503
|
+
deployment_id: String2({ pattern: "^[A-Za-z0-9_-]{1,128}$" }),
|
|
10504
|
+
release_id: sha256,
|
|
10505
|
+
expected_active_release_id: String2({ minLength: 1 }),
|
|
10506
|
+
expected_activation_id: String2({ minLength: 1 }),
|
|
10507
|
+
mutation_id: ApplicationActivationIdSchema
|
|
10508
|
+
}, closed3);
|
|
10509
|
+
var ApplicationDeployRequestSchema = _Object_({
|
|
10510
|
+
mutation_id: ApplicationActivationIdSchema,
|
|
10511
|
+
release_id: sha256,
|
|
10501
10512
|
configuration_id: Optional(ApplicationConfigurationIdSchema),
|
|
10502
10513
|
configuration_write: Optional(ApplicationConfigurationWriteSchema),
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10514
|
+
expected_plan_sha256: Optional(sha256),
|
|
10515
|
+
approved_migration_digest: Optional(sha256),
|
|
10516
|
+
web: Optional(ApplicationDeployWebRequestSchema)
|
|
10506
10517
|
}, closed3);
|
|
10507
|
-
var
|
|
10508
|
-
schema: Literal("supacloud.application-
|
|
10518
|
+
var ApplicationBackendReceiptSchema = _Object_({
|
|
10519
|
+
schema: Literal("supacloud.application-backend-receipt.v1"),
|
|
10509
10520
|
project_ref: String2({ pattern: "^[a-z0-9-]{1,20}$" }),
|
|
10510
10521
|
application_id: ApplicationIdSchema,
|
|
10511
10522
|
environment_id: ApplicationIdSchema,
|
|
10512
|
-
release_id:
|
|
10523
|
+
release_id: sha256,
|
|
10524
|
+
manifest_sha256: sha256,
|
|
10525
|
+
mutation_id: ApplicationActivationIdSchema,
|
|
10526
|
+
plan_sha256: sha256,
|
|
10527
|
+
backup_id: Union([String2(), Null()]),
|
|
10528
|
+
before_ledger_digest: sha256,
|
|
10529
|
+
after_ledger_digest: sha256,
|
|
10530
|
+
applied_versions: _Array_(String2({ pattern: "^\\d{1,20}$" }), { uniqueItems: true }),
|
|
10531
|
+
schema_reloaded: Boolean2(),
|
|
10532
|
+
postgrest_healthy: Boolean2()
|
|
10533
|
+
}, closed3);
|
|
10534
|
+
var ApplicationDeployResultSchema = _Object_({
|
|
10535
|
+
schema: Literal("supacloud.application-deploy.v1"),
|
|
10536
|
+
project_ref: String2({ pattern: "^[a-z0-9-]{1,20}$" }),
|
|
10537
|
+
application_id: ApplicationIdSchema,
|
|
10538
|
+
environment_id: ApplicationIdSchema,
|
|
10539
|
+
mutation_id: ApplicationActivationIdSchema,
|
|
10540
|
+
release_id: sha256,
|
|
10513
10541
|
configuration_id: ApplicationConfigurationIdSchema,
|
|
10514
10542
|
activation_id: ApplicationActivationIdSchema,
|
|
10515
10543
|
expected_activation_id: nullableActivation,
|
|
10516
|
-
plan_sha256:
|
|
10544
|
+
plan_sha256: sha256,
|
|
10517
10545
|
action: Union([Literal("activate"), Literal("no-op")]),
|
|
10518
10546
|
replayed: Boolean2(),
|
|
10519
|
-
execution_performed: Boolean2()
|
|
10547
|
+
execution_performed: Boolean2(),
|
|
10548
|
+
backend_receipt: ApplicationBackendReceiptSchema,
|
|
10549
|
+
web: Optional(_Object_({
|
|
10550
|
+
deployment_id: String2({ pattern: "^[A-Za-z0-9_-]{1,128}$" }),
|
|
10551
|
+
release_id: sha256,
|
|
10552
|
+
activation_id: ApplicationActivationIdSchema,
|
|
10553
|
+
replayed: Boolean2()
|
|
10554
|
+
}, closed3))
|
|
10520
10555
|
}, closed3);
|
|
10521
|
-
function
|
|
10522
|
-
if (!Check2(
|
|
10523
|
-
throw new Error("Invalid application
|
|
10556
|
+
function parseApplicationDeployResult(value) {
|
|
10557
|
+
if (!Check2(ApplicationDeployResultSchema, value)) {
|
|
10558
|
+
throw new Error("Invalid application deploy result");
|
|
10524
10559
|
}
|
|
10525
10560
|
return structuredClone(value);
|
|
10526
10561
|
}
|
|
@@ -10721,7 +10756,7 @@ function parseApplicationReleaseTransferResult(candidate) {
|
|
|
10721
10756
|
}
|
|
10722
10757
|
// node_modules/@supacloud/delivery/src/deployment-evidence.ts
|
|
10723
10758
|
var closed5 = { additionalProperties: false };
|
|
10724
|
-
var
|
|
10759
|
+
var sha2562 = String2({ pattern: "^[a-f0-9]{64}$" });
|
|
10725
10760
|
var isoDate = String2({ minLength: 20, maxLength: 40 });
|
|
10726
10761
|
var status = Union([
|
|
10727
10762
|
Literal("confirmed"),
|
|
@@ -10741,7 +10776,7 @@ var DatabaseProviderSchema = _Object_({
|
|
|
10741
10776
|
topology: Union([Literal("single-node"), Literal("distributed")]),
|
|
10742
10777
|
migration: _Object_({
|
|
10743
10778
|
status,
|
|
10744
|
-
inventory_sha256: Union([
|
|
10779
|
+
inventory_sha256: Union([sha2562, Null()]),
|
|
10745
10780
|
compatibility: Union([
|
|
10746
10781
|
Literal("verified"),
|
|
10747
10782
|
Literal("not-proven"),
|
|
@@ -10789,9 +10824,9 @@ var DeploymentEvidenceSchema = _Object_({
|
|
|
10789
10824
|
}, closed5),
|
|
10790
10825
|
source: _Object_({
|
|
10791
10826
|
commit_sha: Union([String2({ pattern: "^[0-9a-f]{7,64}$" }), Null()]),
|
|
10792
|
-
manifest_sha256:
|
|
10827
|
+
manifest_sha256: sha2562,
|
|
10793
10828
|
contract_schema: Union([String2({ minLength: 1, maxLength: 128 }), Null()]),
|
|
10794
|
-
environment_binding_version: Union([
|
|
10829
|
+
environment_binding_version: Union([sha2562, Null()])
|
|
10795
10830
|
}, closed5),
|
|
10796
10831
|
database: DatabaseProviderSchema,
|
|
10797
10832
|
components: _Array_(DeploymentComponentEvidenceSchema, { minItems: 1, maxItems: 32 }),
|
|
@@ -10847,9 +10882,9 @@ function parseDeploymentEvidence(value) {
|
|
|
10847
10882
|
// node_modules/@supacloud/delivery/src/application-promotion.ts
|
|
10848
10883
|
var closed6 = { additionalProperties: false };
|
|
10849
10884
|
var projectRef2 = String2({ pattern: "^[a-z0-9-]{1,20}$" });
|
|
10850
|
-
var
|
|
10885
|
+
var sha2563 = ApplicationReleaseIdSchema;
|
|
10851
10886
|
var nullableActivation2 = Union([ApplicationActivationIdSchema, Null()]);
|
|
10852
|
-
var nullableDigest = Union([
|
|
10887
|
+
var nullableDigest = Union([sha2563, Null()]);
|
|
10853
10888
|
var observation = {
|
|
10854
10889
|
activation_id: nullableActivation2,
|
|
10855
10890
|
receipt_confirmed: Boolean2(),
|
|
@@ -10875,24 +10910,24 @@ var ApplicationPromotionPlanSchema = _Object_({
|
|
|
10875
10910
|
project_ref: projectRef2,
|
|
10876
10911
|
application_id: ApplicationIdSchema,
|
|
10877
10912
|
environment_id: ApplicationIdSchema,
|
|
10878
|
-
manifest_sha256:
|
|
10879
|
-
plan_sha256:
|
|
10913
|
+
manifest_sha256: sha2563,
|
|
10914
|
+
plan_sha256: sha2563,
|
|
10880
10915
|
source: _Object_({
|
|
10881
10916
|
project_ref: projectRef2,
|
|
10882
10917
|
environment_id: ApplicationIdSchema,
|
|
10883
|
-
release_id:
|
|
10918
|
+
release_id: sha2563,
|
|
10884
10919
|
...observation
|
|
10885
10920
|
}, closed6),
|
|
10886
10921
|
target: _Object_({
|
|
10887
|
-
candidate_release_id:
|
|
10888
|
-
current_release_id: Union([
|
|
10922
|
+
candidate_release_id: sha2563,
|
|
10923
|
+
current_release_id: Union([sha2563, Null()]),
|
|
10889
10924
|
artifact_action: Union([Literal("materialize"), Literal("reuse")]),
|
|
10890
10925
|
configuration_id: Union([ApplicationConfigurationIdSchema, Null()]),
|
|
10891
10926
|
configuration: Union([ApplicationConfigurationViewSchema, Null()]),
|
|
10892
10927
|
...observation
|
|
10893
10928
|
}, closed6),
|
|
10894
10929
|
migrations: _Object_({
|
|
10895
|
-
ledger_digest:
|
|
10930
|
+
ledger_digest: sha2563,
|
|
10896
10931
|
ledger_compatible: Boolean2(),
|
|
10897
10932
|
project_migrations_applied: Boolean2(),
|
|
10898
10933
|
pending_versions: _Array_(String2({ pattern: "^\\d{1,20}$" }), { maxItems: 4096, uniqueItems: true }),
|
|
@@ -16857,7 +16892,8 @@ var ApplicationActivationRetirementResultSchema2 = ApplicationActivationRetireme
|
|
|
16857
16892
|
var ApplicationRollbackSnapshotSchema2 = ApplicationRollbackSnapshotSchema;
|
|
16858
16893
|
var ApplicationActivationHistoryCursorSchema2 = ApplicationActivationHistoryCursorSchema;
|
|
16859
16894
|
var ApplicationPromotionPlanSchema2 = ApplicationPromotionPlanSchema;
|
|
16860
|
-
var
|
|
16895
|
+
var ApplicationDeployRequestSchema2 = ApplicationDeployRequestSchema;
|
|
16896
|
+
var ApplicationDeployWebRequestSchema2 = ApplicationDeployWebRequestSchema;
|
|
16861
16897
|
|
|
16862
16898
|
// src/shared/mutation-protocol.ts
|
|
16863
16899
|
import { createHash as createHash6 } from "node:crypto";
|
|
@@ -17645,7 +17681,7 @@ var APPLICATION_TOOL_SCHEMA = {
|
|
|
17645
17681
|
"put_configuration",
|
|
17646
17682
|
"get_deploy_plan",
|
|
17647
17683
|
"deploy_release",
|
|
17648
|
-
"
|
|
17684
|
+
"deploy_application",
|
|
17649
17685
|
"get_release_transfer_plan",
|
|
17650
17686
|
"transfer_release",
|
|
17651
17687
|
"get_promotion_plan",
|
|
@@ -17673,14 +17709,15 @@ var APPLICATION_TOOL_SCHEMA = {
|
|
|
17673
17709
|
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
17710
|
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
17711
|
configuration_path: optional(String2(), "[put_configuration] Local configuration write JSON including revision and expected revision"),
|
|
17676
|
-
configuration_write: optional(Unknown(), "[
|
|
17712
|
+
configuration_write: optional(Unknown(), "[deploy_application] Parsed immutable configuration write"),
|
|
17677
17713
|
manifest_path: optional(String2(), "[upload_release] Local delivery.manifest.json"),
|
|
17714
|
+
web: optional(ApplicationDeployWebRequestSchema2, "[deploy_application] Immutable staged Web release binding"),
|
|
17678
17715
|
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
17716
|
source_ref: optional(String2({ pattern: "^[A-Za-z0-9_-]{1,20}$" }), "[get_release_transfer_plan/transfer_release] Source project ref"),
|
|
17680
17717
|
source_release_id: optional(ApplicationReleaseIdSchema2, "[get_release_transfer_plan/transfer_release] Source immutable release ID"),
|
|
17681
17718
|
source_environment_id: optional(ApplicationIdSchema2, "[get_promotion_plan] Source environment"),
|
|
17682
|
-
mutation_id: optional(ApplicationActivationIdSchema2, "[promote_application/get_promotion_status/reconcile_promotion] Stable
|
|
17683
|
-
approved_migration_digest: optional(ApplicationReleaseIdSchema2, "[promote_application] Exact reviewed migration execution-plan digest"),
|
|
17719
|
+
mutation_id: optional(ApplicationActivationIdSchema2, "[deploy_application/promote_application/get_promotion_status/reconcile_promotion] Stable mutation ID"),
|
|
17720
|
+
approved_migration_digest: optional(ApplicationReleaseIdSchema2, "[deploy_application/promote_application] Exact reviewed migration execution-plan digest"),
|
|
17684
17721
|
plan: optional(ApplicationPromotionPlanSchema2, "[promote_application] Immutable plan returned by get_promotion_plan"),
|
|
17685
17722
|
cursor: optional(Union([ApplicationReleaseIdSchema2, ApplicationActivationHistoryCursorSchema2]), "[list_releases/get_history] Returned page cursor"),
|
|
17686
17723
|
limit: optional(Integer({ minimum: 1, maximum: 100 }), "[list_releases/get_history] Page size, default 50/20"),
|
|
@@ -18033,57 +18070,50 @@ function registerApplicationTools(server, http) {
|
|
|
18033
18070
|
const observed = await readDeployPlan(http, args, project);
|
|
18034
18071
|
return observed.failure ?? releaseControlSuccess(operation, { ...observed.identity, plan: observed.plan });
|
|
18035
18072
|
}
|
|
18036
|
-
if (action === "
|
|
18073
|
+
if (action === "deploy_application") {
|
|
18037
18074
|
const environmentId = text(args, "environment_id");
|
|
18038
18075
|
const releaseId = text(args, "release_id");
|
|
18039
|
-
|
|
18040
|
-
|
|
18076
|
+
const mutationId = text(args, "mutation_id");
|
|
18077
|
+
if (!Check2(ApplicationIdSchema2, environmentId) || !Check2(ApplicationReleaseIdSchema2, releaseId) || !Check2(ApplicationActivationIdSchema2, mutationId)) {
|
|
18078
|
+
throw new Error("Invalid application deploy identity");
|
|
18041
18079
|
}
|
|
18042
18080
|
const configurationId = args["configuration_id"];
|
|
18043
|
-
const activationId = args["activation_id"];
|
|
18044
|
-
const expectedActivationId = args["expected_activation_id"];
|
|
18045
18081
|
if (configurationId !== undefined && !Check2(ApplicationConfigurationIdSchema2, configurationId)) {
|
|
18046
18082
|
throw new Error("Invalid application configuration ID");
|
|
18047
18083
|
}
|
|
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
18084
|
const configurationWrite = args["configuration_write"];
|
|
18055
18085
|
const parsedConfigurationWrite = configurationWrite === undefined ? undefined : parseApplicationConfigurationWrite(configurationWrite);
|
|
18056
18086
|
const requestedConfigurationId = configurationId ?? parsedConfigurationWrite?.configuration_id;
|
|
18057
18087
|
const body = {
|
|
18088
|
+
mutation_id: mutationId,
|
|
18058
18089
|
release_id: releaseId,
|
|
18059
18090
|
...configurationId === undefined ? {} : { configuration_id: configurationId },
|
|
18060
18091
|
...configurationWrite === undefined ? {} : { configuration_write: configurationWrite },
|
|
18061
|
-
...
|
|
18062
|
-
...
|
|
18063
|
-
expected_activation_id: expectedActivationId === "absent" ? null : expectedActivationId
|
|
18064
|
-
}
|
|
18092
|
+
...typeof args["approved_migration_digest"] === "string" ? { approved_migration_digest: args["approved_migration_digest"] } : {},
|
|
18093
|
+
...args["web"] === undefined ? {} : { web: args["web"] }
|
|
18065
18094
|
};
|
|
18066
|
-
if (!Check2(
|
|
18067
|
-
throw new Error("Invalid application
|
|
18095
|
+
if (!Check2(ApplicationDeployRequestSchema2, body)) {
|
|
18096
|
+
throw new Error("Invalid application deploy request");
|
|
18068
18097
|
}
|
|
18069
|
-
const result = await http.post(`/v1/projects/${project}/applications/${encodeURIComponent(id)}/environments/${encodeURIComponent(environmentId)}/
|
|
18098
|
+
const result = await http.post(`/v1/projects/${project}/applications/${encodeURIComponent(id)}/environments/${encodeURIComponent(environmentId)}/deploy`, body, { timeoutMs: 120000, maxJsonBytes: 131072, responseTimeoutMs: 30000 });
|
|
18070
18099
|
const identity = {
|
|
18071
18100
|
project_ref: ref,
|
|
18072
18101
|
application_id: id,
|
|
18073
18102
|
environment_id: environmentId,
|
|
18074
18103
|
release_id: releaseId,
|
|
18075
|
-
|
|
18076
|
-
...
|
|
18104
|
+
mutation_id: mutationId,
|
|
18105
|
+
...requestedConfigurationId === undefined ? {} : { configuration_id: requestedConfigurationId }
|
|
18077
18106
|
};
|
|
18078
18107
|
if (!result.ok)
|
|
18079
18108
|
return releaseControlMutationFailure(operation, result, identity);
|
|
18080
18109
|
try {
|
|
18081
18110
|
if (result.status !== 200)
|
|
18082
18111
|
throw new Error;
|
|
18083
|
-
const
|
|
18084
|
-
if (
|
|
18112
|
+
const deploy = parseApplicationDeployResult(result.data);
|
|
18113
|
+
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
18114
|
throw new Error;
|
|
18086
|
-
|
|
18115
|
+
}
|
|
18116
|
+
return releaseControlSuccess(operation, deploy);
|
|
18087
18117
|
} catch {
|
|
18088
18118
|
return releaseControlFailure(operation, "OUTCOME_UNKNOWN", result.status, identity);
|
|
18089
18119
|
}
|
|
@@ -24538,6 +24568,7 @@ var REMOTE_APP_DESCRIPTIONS = {
|
|
|
24538
24568
|
source_environment_id: "[promote-plan/diff/promote] Source environment ID",
|
|
24539
24569
|
mutation_id: "[promote-status/promote-reconcile] Stable promotion mutation ID",
|
|
24540
24570
|
approved_migration_digest: "[promote] Exact reviewed migration execution-plan digest",
|
|
24571
|
+
web: "[release] Immutable staged Web release binding prepared by the deploy profile",
|
|
24541
24572
|
branch_ref: "[preview-plan] Preview branch identity",
|
|
24542
24573
|
branch_name: "[preview/preview-create] Human-readable preview branch name",
|
|
24543
24574
|
data_mode: "[preview-plan/preview/preview-create] schema_only or full_clone",
|
|
@@ -25506,9 +25537,6 @@ async function runAppRelease(request, options, delegate) {
|
|
|
25506
25537
|
if (request.configuration_path !== undefined && request.configuration_id !== undefined) {
|
|
25507
25538
|
throw new Error("app release accepts either --configuration_path or --configuration_id, not both");
|
|
25508
25539
|
}
|
|
25509
|
-
if (request.configuration_path === undefined && request.configuration_id === undefined) {
|
|
25510
|
-
throw new Error("app release requires --configuration_path or --configuration_id");
|
|
25511
|
-
}
|
|
25512
25540
|
if (request.activation_id !== undefined || request.expected_activation_id !== undefined) {
|
|
25513
25541
|
throw new Error("app release generates one activation identity; use app deploy for an exact retry");
|
|
25514
25542
|
}
|
|
@@ -25562,25 +25590,36 @@ async function runAppRelease(request, options, delegate) {
|
|
|
25562
25590
|
configurationWrite = parseApplicationConfigurationWrite(await Bun.file(configurationPath).json());
|
|
25563
25591
|
configurationId = configurationWrite.configuration_id;
|
|
25564
25592
|
} catch {
|
|
25565
|
-
return releasePhaseFailure("configure", releaseControlFailure("applications.
|
|
25593
|
+
return releasePhaseFailure("configure", releaseControlFailure("applications.deploy_application", "INVALID_RESPONSE", null, { project_ref: ref, application_id: id, environment_id: environmentId }), completed);
|
|
25566
25594
|
}
|
|
25567
25595
|
completed.configure = { delegated: true, configuration_id: configurationId };
|
|
25568
25596
|
} else {
|
|
25569
|
-
completed.configure = {
|
|
25597
|
+
completed.configure = {
|
|
25598
|
+
delegated: false,
|
|
25599
|
+
resolved_by: "platform",
|
|
25600
|
+
...configurationId === undefined ? {} : { configuration_id: configurationId }
|
|
25601
|
+
};
|
|
25570
25602
|
}
|
|
25571
25603
|
const deployArgs = {
|
|
25572
|
-
action: "
|
|
25604
|
+
action: "deploy_application",
|
|
25573
25605
|
ref,
|
|
25574
25606
|
id,
|
|
25575
25607
|
environment_id: environmentId,
|
|
25576
25608
|
release_id: releaseId,
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
...configurationWrite === undefined ? {} : { configuration_write: configurationWrite }
|
|
25609
|
+
mutation_id: activationId,
|
|
25610
|
+
...configurationId === undefined ? {} : { configuration_id: configurationId },
|
|
25611
|
+
...configurationWrite === undefined ? {} : { configuration_write: configurationWrite },
|
|
25612
|
+
...request.web === undefined ? {} : { web: request.web }
|
|
25580
25613
|
};
|
|
25581
25614
|
validateToolArguments(APPLICATION_TOOL_SCHEMA, deployArgs);
|
|
25582
25615
|
const deployed = await delegate(deployArgs);
|
|
25583
25616
|
const deployReceipt = resultObject(deployed);
|
|
25617
|
+
const resolvedConfigurationId = typeof deployReceipt?.configuration_id === "string" ? deployReceipt.configuration_id : undefined;
|
|
25618
|
+
if (resolvedConfigurationId !== undefined) {
|
|
25619
|
+
configurationId = resolvedConfigurationId;
|
|
25620
|
+
const configure = completed.configure;
|
|
25621
|
+
completed.configure = configure && typeof configure === "object" && !Array.isArray(configure) ? { ...configure, configuration_id: resolvedConfigurationId } : { delegated: false, resolved_by: "platform", configuration_id: resolvedConfigurationId };
|
|
25622
|
+
}
|
|
25584
25623
|
if (deployed.isError || !deployReceipt?.ok) {
|
|
25585
25624
|
return releasePhaseFailure("deploy", deployed, completed);
|
|
25586
25625
|
}
|
|
@@ -25593,13 +25632,14 @@ async function runAppRelease(request, options, delegate) {
|
|
|
25593
25632
|
application_id: id,
|
|
25594
25633
|
environment_id: environmentId,
|
|
25595
25634
|
release_id: releaseId,
|
|
25596
|
-
configuration_id: configurationId,
|
|
25635
|
+
...configurationId === undefined ? {} : { configuration_id: configurationId },
|
|
25597
25636
|
manifest_path: manifestPath,
|
|
25598
25637
|
build: buildReceipt ? completed.build : { skipped: true, manifest_path: manifestPath },
|
|
25599
25638
|
phases: completed,
|
|
25600
25639
|
no_op: deployReceipt.action === "no-op" || deployReceipt.no_op === true,
|
|
25601
25640
|
execution_performed: deployReceipt.execution_performed === true,
|
|
25602
|
-
...typeof deployReceipt.activation_id === "string" ? { activation_id: deployReceipt.activation_id } : {}
|
|
25641
|
+
...typeof deployReceipt.activation_id === "string" ? { activation_id: deployReceipt.activation_id } : {},
|
|
25642
|
+
...deployReceipt.web === undefined ? {} : { web: deployReceipt.web }
|
|
25603
25643
|
};
|
|
25604
25644
|
return request.format === "json" || request.json === true ? textResult(JSON.stringify(result, null, 2)) : textResult([
|
|
25605
25645
|
`${ref}/${id}/${environmentId}: ${result.no_op ? "no-op" : "released"}`,
|
|
@@ -27205,6 +27245,7 @@ import { createHash as createHash8 } from "node:crypto";
|
|
|
27205
27245
|
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "node:fs";
|
|
27206
27246
|
import { dirname as dirname10, join as join16, relative as relative9, resolve as resolve21 } from "node:path";
|
|
27207
27247
|
var SAFE_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
|
|
27248
|
+
var SAFE_ENVIRONMENT_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
27208
27249
|
var BACKUP_ID = /^logical-full_[A-Za-z0-9_-]{1,64}_[a-f0-9]{32}$/;
|
|
27209
27250
|
var SHA256 = /^[a-f0-9]{64}$/;
|
|
27210
27251
|
var SAFE_DATABASE = /^[^\u0000-\u001f\u007f]{1,128}$/;
|
|
@@ -27445,6 +27486,27 @@ function genericScopedReleaseScopeJson(scope) {
|
|
|
27445
27486
|
function genericScopedReleaseScopeSha256(scope) {
|
|
27446
27487
|
return createHash8("sha256").update(genericScopedReleaseScopeJson(scope)).digest("hex");
|
|
27447
27488
|
}
|
|
27489
|
+
function scopeEnvironmentId(scope, action) {
|
|
27490
|
+
const environmentId = isRecord6(scope) ? scope.targetEnvironmentId : undefined;
|
|
27491
|
+
if (typeof environmentId !== "string" || !SAFE_ENVIRONMENT_ID.test(environmentId)) {
|
|
27492
|
+
throw new Error(`Scope for ${action} must be regenerated with a concrete targetEnvironmentId`);
|
|
27493
|
+
}
|
|
27494
|
+
return environmentId;
|
|
27495
|
+
}
|
|
27496
|
+
function requestedScopeEnvironment(args, action) {
|
|
27497
|
+
const environmentId = args.environment_id;
|
|
27498
|
+
if (typeof environmentId !== "string" || !SAFE_ENVIRONMENT_ID.test(environmentId)) {
|
|
27499
|
+
throw new Error(`'environment_id' is required for ${action} and must identify one deployment environment`);
|
|
27500
|
+
}
|
|
27501
|
+
return environmentId;
|
|
27502
|
+
}
|
|
27503
|
+
function assertScopeEnvironment(scope, requestedEnvironmentId, action) {
|
|
27504
|
+
const targetEnvironmentId = scopeEnvironmentId(scope, action);
|
|
27505
|
+
if (targetEnvironmentId !== requestedEnvironmentId) {
|
|
27506
|
+
throw new Error(`Scope target environment '${targetEnvironmentId}' does not match requested environment '${requestedEnvironmentId}'`);
|
|
27507
|
+
}
|
|
27508
|
+
return targetEnvironmentId;
|
|
27509
|
+
}
|
|
27448
27510
|
function runGitRevParse(cwd, args) {
|
|
27449
27511
|
const res = Bun.spawnSync(["git", ...args], { cwd });
|
|
27450
27512
|
if (res.exitCode !== 0 || !res.stdout)
|
|
@@ -27571,10 +27633,10 @@ function collectScopeFiles(cwd, file, files, dir, task) {
|
|
|
27571
27633
|
function handleScopeRebind(args) {
|
|
27572
27634
|
const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
|
|
27573
27635
|
const targetBaseCommit = resolveGitBaseCommit(cwd, args.base_commit);
|
|
27636
|
+
const requestedEnvironmentId = requestedScopeEnvironment(args, "scope_rebind");
|
|
27574
27637
|
const scopeFiles = collectScopeFiles(cwd, args.file, args.files, args.dir, args.task);
|
|
27575
27638
|
const dryRun = args.dry_run === true;
|
|
27576
|
-
const
|
|
27577
|
-
for (const filePath of scopeFiles) {
|
|
27639
|
+
const parsedScopes = scopeFiles.map((filePath) => {
|
|
27578
27640
|
let content;
|
|
27579
27641
|
try {
|
|
27580
27642
|
content = readFileSync10(filePath, "utf8");
|
|
@@ -27587,15 +27649,20 @@ function handleScopeRebind(args) {
|
|
|
27587
27649
|
} catch {
|
|
27588
27650
|
throw new Error(`Scope file '${filePath}' is not valid JSON`);
|
|
27589
27651
|
}
|
|
27590
|
-
|
|
27652
|
+
const targetEnvironmentId = assertScopeEnvironment(parsed, requestedEnvironmentId, "scope_rebind");
|
|
27653
|
+
const previousBaseCommit = parsed.baseCommit;
|
|
27654
|
+
if (typeof previousBaseCommit !== "string" || !/^[0-9a-f]{40}$/.test(previousBaseCommit)) {
|
|
27591
27655
|
throw new Error(`Scope file '${filePath}' has missing or invalid baseCommit`);
|
|
27592
27656
|
}
|
|
27657
|
+
return { filePath, parsed, targetEnvironmentId };
|
|
27658
|
+
});
|
|
27659
|
+
const scopes = [];
|
|
27660
|
+
for (const { filePath, parsed, targetEnvironmentId } of parsedScopes) {
|
|
27593
27661
|
const previousBaseCommit = parsed.baseCommit;
|
|
27594
27662
|
const updated = previousBaseCommit !== targetBaseCommit;
|
|
27595
27663
|
if (updated) {
|
|
27596
27664
|
parsed.baseCommit = targetBaseCommit;
|
|
27597
27665
|
}
|
|
27598
|
-
const canonicalJson = genericScopedReleaseScopeJson(parsed);
|
|
27599
27666
|
const scopeSha256 = genericScopedReleaseScopeSha256(parsed);
|
|
27600
27667
|
if (updated && !dryRun) {
|
|
27601
27668
|
const formatted = JSON.stringify(canonicalValue(parsed), null, 2) + `
|
|
@@ -27608,6 +27675,7 @@ function handleScopeRebind(args) {
|
|
|
27608
27675
|
target_project_ref: typeof parsed.targetProjectRef === "string" ? parsed.targetProjectRef : "",
|
|
27609
27676
|
previous_base_commit: previousBaseCommit,
|
|
27610
27677
|
new_base_commit: targetBaseCommit,
|
|
27678
|
+
target_environment_id: targetEnvironmentId,
|
|
27611
27679
|
scope_sha256: scopeSha256,
|
|
27612
27680
|
updated
|
|
27613
27681
|
});
|
|
@@ -27622,6 +27690,7 @@ function handleScopeRebind(args) {
|
|
|
27622
27690
|
}
|
|
27623
27691
|
function handleScopeInspect(args) {
|
|
27624
27692
|
const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
|
|
27693
|
+
const requestedEnvironmentId = requestedScopeEnvironment(args, "scope_inspect");
|
|
27625
27694
|
if (typeof args.file !== "string" || !args.file.trim()) {
|
|
27626
27695
|
throw new Error("'file' is required for scope_inspect");
|
|
27627
27696
|
}
|
|
@@ -27651,7 +27720,11 @@ function handleScopeInspect(args) {
|
|
|
27651
27720
|
} catch {
|
|
27652
27721
|
throw new Error(`Scope file '${args.file}' is not valid JSON`);
|
|
27653
27722
|
}
|
|
27654
|
-
if (!isRecord6(parsed)
|
|
27723
|
+
if (!isRecord6(parsed)) {
|
|
27724
|
+
throw new Error(`Scope file '${args.file}' must be an object and must be regenerated`);
|
|
27725
|
+
}
|
|
27726
|
+
const targetEnvironmentId = assertScopeEnvironment(parsed, requestedEnvironmentId, "scope_inspect");
|
|
27727
|
+
if (typeof parsed.baseCommit !== "string" || !/^[0-9a-f]{40}$/.test(parsed.baseCommit)) {
|
|
27655
27728
|
throw new Error(`Scope file '${args.file}' has missing or invalid baseCommit`);
|
|
27656
27729
|
}
|
|
27657
27730
|
const scopeSha256 = genericScopedReleaseScopeSha256(parsed);
|
|
@@ -27661,6 +27734,7 @@ function handleScopeInspect(args) {
|
|
|
27661
27734
|
scope: {
|
|
27662
27735
|
task_id: parsed.taskId,
|
|
27663
27736
|
target_project_ref: parsed.targetProjectRef,
|
|
27737
|
+
target_environment_id: targetEnvironmentId,
|
|
27664
27738
|
base_commit: parsed.baseCommit,
|
|
27665
27739
|
functions: parsed.functions,
|
|
27666
27740
|
excluded_functions: parsed.excludedFunctions,
|
|
@@ -27698,6 +27772,7 @@ function handleScopeCreate(args, fallbackRef) {
|
|
|
27698
27772
|
if (typeof args.task !== "string" || !args.task.trim()) {
|
|
27699
27773
|
throw new Error("'task' is required for scope_create");
|
|
27700
27774
|
}
|
|
27775
|
+
const targetEnvironmentId = requestedScopeEnvironment(args, "scope_create");
|
|
27701
27776
|
const projectRef = typeof args.ref === "string" && args.ref.trim() || fallbackRef;
|
|
27702
27777
|
if (!projectRef) {
|
|
27703
27778
|
throw new Error("'ref' is required for scope_create");
|
|
@@ -27714,6 +27789,7 @@ function handleScopeCreate(args, fallbackRef) {
|
|
|
27714
27789
|
functions: parseCommaList(args.functions),
|
|
27715
27790
|
migrations: parseCommaList(args.migrations),
|
|
27716
27791
|
targetProjectRef: projectRef,
|
|
27792
|
+
targetEnvironmentId,
|
|
27717
27793
|
taskId: String(args.task).trim(),
|
|
27718
27794
|
web: Boolean(args.web)
|
|
27719
27795
|
};
|
|
@@ -27726,6 +27802,7 @@ function handleScopeCreate(args, fallbackRef) {
|
|
|
27726
27802
|
file: relative9(cwd, filePath),
|
|
27727
27803
|
task_id: scope.taskId,
|
|
27728
27804
|
target_project_ref: projectRef,
|
|
27805
|
+
target_environment_id: targetEnvironmentId,
|
|
27729
27806
|
base_commit: targetBaseCommit,
|
|
27730
27807
|
scope_sha256: scopeSha256,
|
|
27731
27808
|
scope
|
|
@@ -27752,6 +27829,7 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
27752
27829
|
dir: optional(String2(), "[scope_rebind] Directory containing release scope JSON files (defaults to supacloud/fa/release-scopes, release-scopes, or scopes)"),
|
|
27753
27830
|
task: optional(String2(), "[scope_rebind/scope_create] Task ID or prefix filter for scope files"),
|
|
27754
27831
|
base_commit: optional(String2(), "[scope_rebind/scope_create] Target 40-character base commit SHA or git ref (defaults to origin/main -> main -> HEAD^ -> HEAD)"),
|
|
27832
|
+
environment_id: optional(String2(), "[scope_inspect/scope_rebind/scope_create] Exact target deployment environment"),
|
|
27755
27833
|
functions: optional(String2(), "[scope_create] Comma-separated list of functions to deploy"),
|
|
27756
27834
|
excluded_functions: optional(String2(), "[scope_create] Comma-separated list of functions to exclude"),
|
|
27757
27835
|
migrations: optional(String2(), "[scope_create] Comma-separated list of migrations to apply"),
|
|
@@ -27948,7 +28026,8 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
27948
28026
|
}
|
|
27949
28027
|
|
|
27950
28028
|
// src/shared/tools/deploy-tools.ts
|
|
27951
|
-
import { access, lstat as lstat5, mkdtemp as mkdtemp2, opendir, rm as rm3 } from "node:fs/promises";
|
|
28029
|
+
import { access, lstat as lstat5, mkdtemp as mkdtemp2, opendir, readFile as readFile6, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
|
|
28030
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
27952
28031
|
import { createHash as createHash9 } from "node:crypto";
|
|
27953
28032
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
27954
28033
|
import { basename as basename8, dirname as dirname11, join as join17, relative as relative10, resolve as resolve22, sep as sep5 } from "node:path";
|
|
@@ -27998,7 +28077,7 @@ function frontendDeployment(candidate) {
|
|
|
27998
28077
|
}
|
|
27999
28078
|
async function readJson(path) {
|
|
28000
28079
|
try {
|
|
28001
|
-
return await
|
|
28080
|
+
return JSON.parse(await readFile6(path, "utf8"));
|
|
28002
28081
|
} catch (error) {
|
|
28003
28082
|
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
28004
28083
|
return null;
|
|
@@ -28153,6 +28232,33 @@ function selectDeployment(deployments, requestedId, projectName) {
|
|
|
28153
28232
|
const ids = deployments.map((deployment) => deployment.id).sort().join(", ");
|
|
28154
28233
|
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
28234
|
}
|
|
28235
|
+
function selectFrontendTarget(configRoot, config) {
|
|
28236
|
+
if (config.targets === undefined && config.frontend === undefined)
|
|
28237
|
+
return null;
|
|
28238
|
+
const targets = config.targets ? Object.entries(config.targets).filter(([, target]) => target.type === "frontend") : [["frontend", {
|
|
28239
|
+
type: "frontend",
|
|
28240
|
+
root: config.frontend?.root,
|
|
28241
|
+
id: config.frontend?.id,
|
|
28242
|
+
buildCommand: config.frontend?.buildCommand,
|
|
28243
|
+
outputDirectory: config.frontend?.outputDirectory,
|
|
28244
|
+
buildEnv: config.frontend?.buildEnv
|
|
28245
|
+
}]];
|
|
28246
|
+
if (targets.length === 0)
|
|
28247
|
+
return null;
|
|
28248
|
+
if (config.defaultTarget) {
|
|
28249
|
+
const selected = targets.find(([name]) => name === config.defaultTarget);
|
|
28250
|
+
if (selected)
|
|
28251
|
+
return { name: selected[0], config: selected[1], root: targetRoot(configRoot, selected[1], selected[0]) };
|
|
28252
|
+
}
|
|
28253
|
+
const namedWeb = targets.find(([name]) => name === "web");
|
|
28254
|
+
if (namedWeb)
|
|
28255
|
+
return { name: namedWeb[0], config: namedWeb[1], root: targetRoot(configRoot, namedWeb[1], namedWeb[0]) };
|
|
28256
|
+
if (targets.length === 1) {
|
|
28257
|
+
const selected = targets[0];
|
|
28258
|
+
return { name: selected[0], config: selected[1], root: targetRoot(configRoot, selected[1], selected[0]) };
|
|
28259
|
+
}
|
|
28260
|
+
throw new Error(`Multiple frontend deploy targets found (${targets.map(([name]) => name).sort().join(", ")}). Set defaultTarget to the Web target`);
|
|
28261
|
+
}
|
|
28156
28262
|
function targetRoot(configRoot, target, name) {
|
|
28157
28263
|
const root = resolve22(configRoot, target.root || (name === "frontend" ? "." : name));
|
|
28158
28264
|
const prefix = `${resolve22(configRoot)}${sep5}`;
|
|
@@ -28217,9 +28323,9 @@ function resolveInsideProject(projectDirectory, candidate, allowRoot = false) {
|
|
|
28217
28323
|
return resolved;
|
|
28218
28324
|
}
|
|
28219
28325
|
function createBuildEnvironment(names, environmentVariables) {
|
|
28220
|
-
const selectedNames = [...new Set(names || [])].sort();
|
|
28221
|
-
const selectedNameSet = new Set(selectedNames);
|
|
28222
28326
|
const values = environmentVariables ?? process.env;
|
|
28327
|
+
const selectedNames = [...new Set(names ?? Object.keys(values).filter(isPublicBuildEnv))].sort();
|
|
28328
|
+
const selectedNameSet = new Set(selectedNames);
|
|
28223
28329
|
const env = Object.fromEntries(Object.entries(process.env).filter(([name]) => !isProtectedBuildEnv(name) && (!isPublicBuildEnv(name) || selectedNameSet.has(name)) && (!SENSITIVE_BUILD_ENV.test(name) || isPublicBuildEnv(name))));
|
|
28224
28330
|
const missing = selectedNames.filter((name) => !values[name]?.trim());
|
|
28225
28331
|
for (const name of selectedNames) {
|
|
@@ -28235,15 +28341,21 @@ function assertBuildEnvironment(build, skipBuild) {
|
|
|
28235
28341
|
}
|
|
28236
28342
|
}
|
|
28237
28343
|
async function runBuild(command, cwd, environment, json) {
|
|
28238
|
-
|
|
28239
|
-
|
|
28240
|
-
|
|
28241
|
-
|
|
28344
|
+
await new Promise((resolve, reject) => {
|
|
28345
|
+
const child = spawn2("sh", ["-c", command], {
|
|
28346
|
+
cwd,
|
|
28347
|
+
env: environment,
|
|
28348
|
+
stdio: ["inherit", json ? 2 : "inherit", "inherit"]
|
|
28349
|
+
});
|
|
28350
|
+
child.once("error", reject);
|
|
28351
|
+
child.once("close", (exitCode) => {
|
|
28352
|
+
if (exitCode === 0) {
|
|
28353
|
+
resolve();
|
|
28354
|
+
} else {
|
|
28355
|
+
reject(new Error(`Build command failed (exit ${exitCode ?? "unknown"}): ${command}`));
|
|
28356
|
+
}
|
|
28357
|
+
});
|
|
28242
28358
|
});
|
|
28243
|
-
const exitCode = await child.exited;
|
|
28244
|
-
if (exitCode !== 0) {
|
|
28245
|
-
throw new Error(`Build command failed (exit ${exitCode ?? "unknown"}): ${command}`);
|
|
28246
|
-
}
|
|
28247
28359
|
}
|
|
28248
28360
|
async function collectArchiveFiles(root) {
|
|
28249
28361
|
const rootStat = await lstat5(root).catch(() => null);
|
|
@@ -28276,7 +28388,7 @@ async function collectArchiveFiles(root) {
|
|
|
28276
28388
|
const files = new Map;
|
|
28277
28389
|
const digests = [];
|
|
28278
28390
|
for (const path of paths) {
|
|
28279
|
-
const data = new Uint8Array(await
|
|
28391
|
+
const data = new Uint8Array(await readFile6(path));
|
|
28280
28392
|
bytes += data.byteLength;
|
|
28281
28393
|
if (bytes > MAX_SOURCE_BYTES)
|
|
28282
28394
|
throw new Error(`Frontend output exceeds ${MAX_SOURCE_BYTES} bytes`);
|
|
@@ -28300,7 +28412,7 @@ async function createFrontendArchive(outputDirectory) {
|
|
|
28300
28412
|
const archivePath = join17(temporaryDirectory, `${basename8(outputDirectory)}.tar.zst`);
|
|
28301
28413
|
try {
|
|
28302
28414
|
const archiveBytes = await createFrontendTarZstd(collected.files);
|
|
28303
|
-
await
|
|
28415
|
+
await writeFile6(archivePath, archiveBytes);
|
|
28304
28416
|
const sha256 = createHash9("sha256").update(archiveBytes).digest("hex");
|
|
28305
28417
|
return {
|
|
28306
28418
|
archivePath,
|
|
@@ -28325,6 +28437,71 @@ function payload(response) {
|
|
|
28325
28437
|
throw new Error("Deployment operation returned an invalid response");
|
|
28326
28438
|
return parsed;
|
|
28327
28439
|
}
|
|
28440
|
+
async function stageApplicationWebRelease(http, projectRef, configRoot, skipBuild, json, environmentVariables) {
|
|
28441
|
+
const target = selectFrontendTarget(configRoot, await readDeployConfig(configRoot));
|
|
28442
|
+
if (!target)
|
|
28443
|
+
return null;
|
|
28444
|
+
const projectDirectory = target.root;
|
|
28445
|
+
const targetState = await lstat5(projectDirectory).catch(() => null);
|
|
28446
|
+
if (!targetState?.isDirectory() || targetState.isSymbolicLink()) {
|
|
28447
|
+
throw new Error(`Deploy target '${target.name}' root is not a regular directory: ${projectDirectory}`);
|
|
28448
|
+
}
|
|
28449
|
+
const requestedId = target.config.id;
|
|
28450
|
+
const listResponse = await http.get(`/v1/projects/${encodeURIComponent(projectRef)}/frontend/deployments`);
|
|
28451
|
+
if (!listResponse.ok)
|
|
28452
|
+
throw new Error(`Failed to list frontend deployments (HTTP ${listResponse.status})`);
|
|
28453
|
+
const selected = selectDeployment(deploymentList(listResponse.data), requestedId, await packageName(projectDirectory));
|
|
28454
|
+
const configuredBuildCommand = String(target.config.buildCommand || selected.buildCommand || "").trim();
|
|
28455
|
+
const buildCommand = configuredBuildCommand || (skipBuild ? "" : await defaultBuildCommand(projectDirectory, configRoot));
|
|
28456
|
+
const configuredOutput = String(target.config.outputDirectory || "").trim();
|
|
28457
|
+
const remoteOutput = selected.outputDirectory && selected.outputDirectory !== "." ? selected.outputDirectory : "";
|
|
28458
|
+
const outputDirectory = resolveInsideProject(projectDirectory, configuredOutput || remoteOutput || defaultOutputDirectory(selected.framework));
|
|
28459
|
+
const build = createBuildEnvironment(target.config.buildEnv, environmentVariables);
|
|
28460
|
+
const report = phaseReporter(json);
|
|
28461
|
+
report("web", `${target.name} (${selected.id})`);
|
|
28462
|
+
assertBuildEnvironment(build, skipBuild);
|
|
28463
|
+
if (!skipBuild) {
|
|
28464
|
+
report("build", buildCommand);
|
|
28465
|
+
await runBuild(buildCommand, projectDirectory, build.env, json);
|
|
28466
|
+
} else {
|
|
28467
|
+
report("build", "skipped");
|
|
28468
|
+
}
|
|
28469
|
+
report("package", relative10(projectDirectory, outputDirectory) || ".");
|
|
28470
|
+
const archive = await createFrontendArchive(outputDirectory);
|
|
28471
|
+
try {
|
|
28472
|
+
const authority = payload(await readFrontendReleaseAuthority(http, projectRef, selected.id));
|
|
28473
|
+
const activeReleaseId = typeof authority.active_release_id === "string" ? authority.active_release_id : null;
|
|
28474
|
+
const activeActivationId = typeof authority.active_activation_id === "string" ? authority.active_activation_id : null;
|
|
28475
|
+
const current = Array.isArray(authority.releases) ? record4(authority.releases[0]) : null;
|
|
28476
|
+
if (archive.sha256 === activeReleaseId && archive.treeSha256 !== current?.tree_sha256) {
|
|
28477
|
+
throw new Error("Active Web release tree does not match the local archive");
|
|
28478
|
+
}
|
|
28479
|
+
if (archive.treeSha256 === current?.tree_sha256) {
|
|
28480
|
+
report("done", "Web already current");
|
|
28481
|
+
return {};
|
|
28482
|
+
}
|
|
28483
|
+
report("upload", `${archive.fileCount} files`);
|
|
28484
|
+
const uploaded = payload(await uploadFrontendRelease(http, projectRef, selected.id, archive.archivePath));
|
|
28485
|
+
const release = record4(uploaded.release);
|
|
28486
|
+
const releaseId = requiredString(release?.release_id, "release.release_id");
|
|
28487
|
+
if (releaseId !== archive.sha256)
|
|
28488
|
+
throw new Error("Uploaded Web release identity does not match the local archive");
|
|
28489
|
+
if (release?.tree_sha256 !== archive.treeSha256)
|
|
28490
|
+
throw new Error("Uploaded Web release tree does not match the local build");
|
|
28491
|
+
report("done", releaseId.slice(0, 12));
|
|
28492
|
+
return {
|
|
28493
|
+
request: {
|
|
28494
|
+
deployment_id: selected.id,
|
|
28495
|
+
release_id: releaseId,
|
|
28496
|
+
expected_active_release_id: activeReleaseId ?? "absent",
|
|
28497
|
+
expected_activation_id: activeActivationId ?? "absent",
|
|
28498
|
+
mutation_id: crypto.randomUUID()
|
|
28499
|
+
}
|
|
28500
|
+
};
|
|
28501
|
+
} finally {
|
|
28502
|
+
await archive.cleanup();
|
|
28503
|
+
}
|
|
28504
|
+
}
|
|
28328
28505
|
function phaseReporter(json) {
|
|
28329
28506
|
const started = performance.now();
|
|
28330
28507
|
return (phase, detail) => {
|
|
@@ -28373,12 +28550,10 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28373
28550
|
}
|
|
28374
28551
|
const configurationPath = options.application.configurationPath?.trim();
|
|
28375
28552
|
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
28553
|
if (configurationPath && configurationId) {
|
|
28380
28554
|
throw new Error("Application profile must set only one of SUPACLOUD_APPLICATION_CONFIGURATION_PATH or SUPACLOUD_APPLICATION_CONFIGURATION_ID");
|
|
28381
28555
|
}
|
|
28556
|
+
const stagedWeb = await stageApplicationWebRelease(http, projectRef, configRoot, args.skip_build === true, args.json === true, options.environmentVariables);
|
|
28382
28557
|
const result = await options.applicationRelease({
|
|
28383
28558
|
action: "release",
|
|
28384
28559
|
ref: projectRef,
|
|
@@ -28389,6 +28564,7 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28389
28564
|
...configurationId ? { configuration_id: configurationId } : {},
|
|
28390
28565
|
...options.application.manifestPath ? { manifest_path: options.application.manifestPath } : {},
|
|
28391
28566
|
...args.skip_build === true ? { skip_build: true } : {},
|
|
28567
|
+
...stagedWeb?.request === undefined ? {} : { web: stagedWeb.request },
|
|
28392
28568
|
...args.json === true ? { format: "json", json: true } : {}
|
|
28393
28569
|
});
|
|
28394
28570
|
return result;
|
|
@@ -28625,7 +28801,7 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28625
28801
|
|
|
28626
28802
|
// src/shared/tools/remote-dev-tools.ts
|
|
28627
28803
|
import { existsSync as existsSync14 } from "node:fs";
|
|
28628
|
-
import { readFile as
|
|
28804
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
28629
28805
|
import { readdir as readdir7 } from "node:fs/promises";
|
|
28630
28806
|
import { createHash as createHash10 } from "node:crypto";
|
|
28631
28807
|
import { join as join18, resolve as resolve23 } from "node:path";
|
|
@@ -28703,7 +28879,7 @@ async function readProjectConfig(root) {
|
|
|
28703
28879
|
if (!existsSync14(file))
|
|
28704
28880
|
return {};
|
|
28705
28881
|
try {
|
|
28706
|
-
const parsed = JSON.parse(await
|
|
28882
|
+
const parsed = JSON.parse(await readFile7(file, "utf8"));
|
|
28707
28883
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
28708
28884
|
} catch (error) {
|
|
28709
28885
|
throw new Error(`Invalid supacloud.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -28729,7 +28905,7 @@ async function sourceFingerprint(root) {
|
|
|
28729
28905
|
const hash = createHash10("sha256");
|
|
28730
28906
|
for (const file of files) {
|
|
28731
28907
|
hash.update(file.slice(root.length).replace(/\\/g, "/"));
|
|
28732
|
-
hash.update(await
|
|
28908
|
+
hash.update(await readFile7(file));
|
|
28733
28909
|
}
|
|
28734
28910
|
return hash.digest("hex");
|
|
28735
28911
|
}
|
|
@@ -28895,7 +29071,7 @@ function registerRemoteDevTools(server, options = {}) {
|
|
|
28895
29071
|
|
|
28896
29072
|
// src/shared/tools/developer-experience-tools.ts
|
|
28897
29073
|
import { existsSync as existsSync15 } from "node:fs";
|
|
28898
|
-
import { mkdir as mkdir7, readdir as readdir8, readFile as
|
|
29074
|
+
import { mkdir as mkdir7, readdir as readdir8, readFile as readFile8, writeFile as writeFile7 } from "node:fs/promises";
|
|
28899
29075
|
import { dirname as dirname12, join as join19, resolve as resolve24 } from "node:path";
|
|
28900
29076
|
var initToolSchema = {
|
|
28901
29077
|
root: optional(String2(), "Project directory to initialize (default: current directory)"),
|
|
@@ -28950,7 +29126,7 @@ async function readPackageManifest(root) {
|
|
|
28950
29126
|
if (!existsSync15(path))
|
|
28951
29127
|
return {};
|
|
28952
29128
|
try {
|
|
28953
|
-
const parsed = JSON.parse(await
|
|
29129
|
+
const parsed = JSON.parse(await readFile8(path, "utf8"));
|
|
28954
29130
|
const value = record5(parsed);
|
|
28955
29131
|
if (!value)
|
|
28956
29132
|
throw new Error("package.json must contain an object");
|
|
@@ -28968,7 +29144,7 @@ async function readPackageManifest(root) {
|
|
|
28968
29144
|
}
|
|
28969
29145
|
async function writeNewFile(path, content) {
|
|
28970
29146
|
try {
|
|
28971
|
-
await
|
|
29147
|
+
await writeFile7(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
28972
29148
|
return true;
|
|
28973
29149
|
} catch (error) {
|
|
28974
29150
|
if (error instanceof Error && "code" in error && error.code === "EEXIST")
|
|
@@ -28978,7 +29154,7 @@ async function writeNewFile(path, content) {
|
|
|
28978
29154
|
}
|
|
28979
29155
|
async function ensureGitignore(root) {
|
|
28980
29156
|
const path = join19(root, ".gitignore");
|
|
28981
|
-
const existing = existsSync15(path) ? await
|
|
29157
|
+
const existing = existsSync15(path) ? await readFile8(path, "utf8") : "";
|
|
28982
29158
|
const required = [".env.supacloud.*", "!.env.supacloud.*.example"];
|
|
28983
29159
|
const missing = required.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
28984
29160
|
if (missing.length === 0)
|
|
@@ -28986,7 +29162,7 @@ async function ensureGitignore(root) {
|
|
|
28986
29162
|
const prefix = existing.length > 0 && !existing.endsWith(`
|
|
28987
29163
|
`) ? `${existing}
|
|
28988
29164
|
` : existing;
|
|
28989
|
-
await
|
|
29165
|
+
await writeFile7(path, `${prefix}${missing.join(`
|
|
28990
29166
|
`)}
|
|
28991
29167
|
`, "utf8");
|
|
28992
29168
|
return true;
|
|
@@ -29009,7 +29185,7 @@ async function initializeProject(request, options) {
|
|
|
29009
29185
|
};
|
|
29010
29186
|
const created = [];
|
|
29011
29187
|
try {
|
|
29012
|
-
await
|
|
29188
|
+
await writeFile7(configPath, `${JSON.stringify(config, null, 2)}
|
|
29013
29189
|
`, {
|
|
29014
29190
|
encoding: "utf8",
|
|
29015
29191
|
flag: "wx",
|
|
@@ -29205,7 +29381,7 @@ function registerDeveloperExperienceTools(server, options = {}) {
|
|
|
29205
29381
|
// package.json
|
|
29206
29382
|
var package_default = {
|
|
29207
29383
|
name: "@supacloud/cli",
|
|
29208
|
-
version: "0.
|
|
29384
|
+
version: "0.90.1",
|
|
29209
29385
|
description: "Project-scoped CLI for SupaCloud users",
|
|
29210
29386
|
type: "module",
|
|
29211
29387
|
main: "./dist/index.js",
|
|
@@ -29258,13 +29434,13 @@ var package_default = {
|
|
|
29258
29434
|
},
|
|
29259
29435
|
dependencies: {
|
|
29260
29436
|
"@supacloud/compiler": "^0.44.1",
|
|
29261
|
-
"@supacloud/delivery": "0.
|
|
29437
|
+
"@supacloud/delivery": "0.15.0",
|
|
29262
29438
|
"@supacloud/db": "0.16.1",
|
|
29263
29439
|
typebox: "1.3.36"
|
|
29264
29440
|
},
|
|
29265
29441
|
overrides: {
|
|
29266
29442
|
"@supacloud/contracts": "0.8.1",
|
|
29267
|
-
"@supacloud/delivery": "0.
|
|
29443
|
+
"@supacloud/delivery": "0.15.0",
|
|
29268
29444
|
"@graphql-tools/utils": "^12.0.3"
|
|
29269
29445
|
},
|
|
29270
29446
|
devDependencies: {
|
|
@@ -29498,8 +29674,6 @@ EXAMPLES
|
|
|
29498
29674
|
${preferredCommand} release postgrest_restart --ref abc123
|
|
29499
29675
|
${preferredCommand} release release_canary_fixture_stage_replay --ref abc123 --subject <uuid> --request_id <uuid>
|
|
29500
29676
|
${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
|
|
29503
29677
|
${preferredCommand} queue stats --queue emails
|
|
29504
29678
|
${preferredCommand} queue dlq --queue emails --limit 20
|
|
29505
29679
|
${preferredCommand} frontend list --ref abc123
|
package/dist/shared/schema.d.ts
CHANGED
|
@@ -8,6 +8,6 @@ export declare function stringEnum<const Values extends readonly [string, ...str
|
|
|
8
8
|
export declare function withDescription<T extends TSchema>(schema: T, description: string): T;
|
|
9
9
|
export declare function optional<T extends TSchema>(schema: T, description?: string): "~optional" extends keyof T ? T : Type.TOptional<T>;
|
|
10
10
|
export declare function decodedSchema<TInput extends TSchema, TOutput extends TSchema>(inputSchema: TInput, outputSchema: TOutput, decode: (input: StaticDecode<TInput>) => unknown, options?: TSchemaOptions): Type.TCodec<TInput, import("typebox").StaticType<[], "Decode", {}, {}, TOutput>>;
|
|
11
|
-
export declare function schemaEnumValues(schema: TSchema): string[];
|
|
11
|
+
export declare function schemaEnumValues(schema: TSchema | undefined): string[];
|
|
12
12
|
export declare function schemaDescription(schema: TSchema): string;
|
|
13
13
|
export declare function schemaProperties(schema: TSchema | ToolSchema): ToolSchema;
|
|
@@ -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
|
|
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
|
|
176
|
-
schema: import("typebox").TLiteral<"supacloud.application-
|
|
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" | "
|
|
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/dist/tool-runtime.js
CHANGED
|
@@ -97,6 +97,8 @@ function decodedSchema(inputSchema, outputSchema, decode, options) {
|
|
|
97
97
|
return result;
|
|
98
98
|
}
|
|
99
99
|
function schemaEnumValues(schema) {
|
|
100
|
+
if (!schema)
|
|
101
|
+
return [];
|
|
100
102
|
const view = schema;
|
|
101
103
|
if (Array.isArray(view.enum)) {
|
|
102
104
|
return view.enum.filter((value) => typeof value === "string" || typeof value === "number").map((value) => String(value));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.90.1",
|
|
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.
|
|
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.
|
|
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;
|
|
27
|
-
| Inspect release scope manifest and git alignment | `release scope_inspect` | Local action;
|
|
28
|
-
| Scaffold a new canonical release scope manifest | `release scope_create` | Local action; outputs
|
|
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
|
|
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
|