@supacloud/cli 0.28.1 → 0.29.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 +17 -4
- package/dist/index.js +78 -9
- package/package.json +1 -1
- package/skills/supacloud-cli/references/command-map.md +2 -0
package/README.md
CHANGED
|
@@ -108,10 +108,12 @@ loopback development origins, with the default `:80` likewise omitted. Use
|
|
|
108
108
|
|
|
109
109
|
### Verified release controls
|
|
110
110
|
|
|
111
|
-
`release` is an official CLI entry point for
|
|
112
|
-
|
|
113
|
-
API context above
|
|
114
|
-
|
|
111
|
+
`release` is an official CLI entry point for verified Management API controls
|
|
112
|
+
and the release-canary fixture receipt replay. Management actions require the
|
|
113
|
+
Management API context above. `release_canary_fixture_stage_replay` additionally
|
|
114
|
+
requires the same project's `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY`; the
|
|
115
|
+
key is sent only to that application origin and is never promoted to Management
|
|
116
|
+
authority.
|
|
115
117
|
|
|
116
118
|
Project pause and restore are explicit lifecycle commands. A logical backup
|
|
117
119
|
restore requires an operator to pause the selected project first and then use
|
|
@@ -138,6 +140,8 @@ supacloud-cli release logical_backup_restore --ref abc123 \
|
|
|
138
140
|
--restore_confirmation RESTORE_PROJECT:abc123:logical-full_abc123_<backup-id-suffix>:<64-lowercase-hex>
|
|
139
141
|
supacloud-cli release postgrest_status --ref abc123
|
|
140
142
|
supacloud-cli release postgrest_restart --ref abc123
|
|
143
|
+
supacloud-cli release release_canary_fixture_stage_replay --ref abc123 \
|
|
144
|
+
--subject <central-subject-uuid> --request_id <stage-request-uuid>
|
|
141
145
|
```
|
|
142
146
|
|
|
143
147
|
Backup creation reports success only after the CLI verifies exactly one new
|
|
@@ -158,6 +162,15 @@ receipt and reads back `desired=running`, `actual=running`, and
|
|
|
158
162
|
`health=healthy`. Both mutating controls follow the normal production
|
|
159
163
|
confirmation and read-only protections.
|
|
160
164
|
|
|
165
|
+
`release_canary_fixture_stage_replay` performs one non-retried, response-bounded
|
|
166
|
+
call to the fixed `fa_release_canary_fixture_stage` PostgREST RPC with both
|
|
167
|
+
bearer and `apikey` service-role headers. It accepts only canonical subject and
|
|
168
|
+
request UUIDs, requires a strict `staged` and `idempotent=true` four-field
|
|
169
|
+
receipt, and emits only that safe projection. Before and after the call, the CLI
|
|
170
|
+
reads the selected project's authoritative endpoint projection and requires the
|
|
171
|
+
configured application origin to match its API origin or alias. Production
|
|
172
|
+
confirmation is mandatory.
|
|
173
|
+
|
|
161
174
|
The legacy `.env` fallback is unclassified and therefore does not enable the
|
|
162
175
|
production confirmation gate. Production automation must select a `prod` or
|
|
163
176
|
`production` profile, or set `SUPACLOUD_ENV=production` together with a complete
|
package/dist/index.js
CHANGED
|
@@ -6491,7 +6491,7 @@ var ACTION_POLICY = {
|
|
|
6491
6491
|
mutations: { read: ["status"] },
|
|
6492
6492
|
release: {
|
|
6493
6493
|
read: ["logical_backup_list", "postgrest_status"],
|
|
6494
|
-
write: ["logical_backup_create", "logical_backup_restore", "postgrest_restart"]
|
|
6494
|
+
write: ["logical_backup_create", "logical_backup_restore", "postgrest_restart", "release_canary_fixture_stage_replay"]
|
|
6495
6495
|
},
|
|
6496
6496
|
secrets: { read: ["list"], write: ["upsert", "delete"] },
|
|
6497
6497
|
frontend: {
|
|
@@ -6805,14 +6805,17 @@ async function responseJsonOrNull(response) {
|
|
|
6805
6805
|
class HttpTransport {
|
|
6806
6806
|
baseUrl;
|
|
6807
6807
|
token;
|
|
6808
|
+
apiKey;
|
|
6808
6809
|
constructor(config) {
|
|
6809
6810
|
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
6810
6811
|
this.token = config.token;
|
|
6812
|
+
this.apiKey = config.apiKey ?? "";
|
|
6811
6813
|
}
|
|
6812
6814
|
headers() {
|
|
6813
6815
|
return {
|
|
6814
6816
|
Authorization: `Bearer ${this.token}`,
|
|
6815
|
-
"Content-Type": "application/json"
|
|
6817
|
+
"Content-Type": "application/json",
|
|
6818
|
+
...this.apiKey ? { apikey: this.apiKey } : {}
|
|
6816
6819
|
};
|
|
6817
6820
|
}
|
|
6818
6821
|
async mutationWithResponseReader(method, path, serializedBody, responseReader, timeoutMs = DEFAULT_TIMEOUT) {
|
|
@@ -10562,6 +10565,15 @@ function projectEndpointRead(response, expectedRef) {
|
|
|
10562
10565
|
const projection = projectEndpointProjection(response.data);
|
|
10563
10566
|
return projection && projection.project_ref === expectedRef ? successfulResult2(projection) : failedResult2("Invalid project endpoint response");
|
|
10564
10567
|
}
|
|
10568
|
+
function projectApiOrigins(response, expectedRef) {
|
|
10569
|
+
if (!successfulResponse2(response))
|
|
10570
|
+
return null;
|
|
10571
|
+
const projection = projectEndpointProjection(response.data);
|
|
10572
|
+
if (!projection || projection.project_ref !== expectedRef)
|
|
10573
|
+
return null;
|
|
10574
|
+
const api = projection.endpoints.api;
|
|
10575
|
+
return [api.origin, ...api.aliases.map((alias) => `${api.scheme}://${alias}`)];
|
|
10576
|
+
}
|
|
10565
10577
|
|
|
10566
10578
|
// src/shared/tools/project-cli-tools.ts
|
|
10567
10579
|
function projectReadResponse(readResult) {
|
|
@@ -12829,6 +12841,9 @@ var INVENTORY_MAX_BYTES = 1024 * 1024;
|
|
|
12829
12841
|
var MUTATION_MAX_BYTES = 64 * 1024;
|
|
12830
12842
|
var BACKUP_TIMEOUT_MS = 36 * 60000;
|
|
12831
12843
|
var RELEASE_READ_RESPONSE_TIMEOUT_MS = 5000;
|
|
12844
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
12845
|
+
var RELEASE_CANARY_TENANT_KEY = /^release-canary-[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
12846
|
+
var RELEASE_CANARY_STAGE_RECEIPT_KEYS = new Set(["fixtureId", "tenantKey", "state", "idempotent"]);
|
|
12832
12847
|
function isRecord3(value) {
|
|
12833
12848
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12834
12849
|
}
|
|
@@ -12961,20 +12976,44 @@ function readPostgrestFailure(operation, read) {
|
|
|
12961
12976
|
function isRestartReceipt(value) {
|
|
12962
12977
|
return isRecord3(value) && value.service === "postgrest" && value.action === "restart" && value.success === true;
|
|
12963
12978
|
}
|
|
12979
|
+
function releaseCanaryStageInput(subject, requestId) {
|
|
12980
|
+
if (typeof subject !== "string" || !UUID.test(subject))
|
|
12981
|
+
throw new Error("'subject' must be a canonical UUID");
|
|
12982
|
+
if (typeof requestId !== "string" || !UUID.test(requestId))
|
|
12983
|
+
throw new Error("'request_id' must be a canonical UUID");
|
|
12984
|
+
return { p_subject: subject, p_request_id: requestId };
|
|
12985
|
+
}
|
|
12986
|
+
function releaseCanaryStageReceipt(value) {
|
|
12987
|
+
if (!isRecord3(value) || Object.keys(value).some((key) => !RELEASE_CANARY_STAGE_RECEIPT_KEYS.has(key)) || Object.keys(value).length !== RELEASE_CANARY_STAGE_RECEIPT_KEYS.size || typeof value.fixtureId !== "string" || !UUID.test(value.fixtureId) || typeof value.tenantKey !== "string" || !RELEASE_CANARY_TENANT_KEY.test(value.tenantKey) || value.state !== "staged" || value.idempotent !== true)
|
|
12988
|
+
return null;
|
|
12989
|
+
return {
|
|
12990
|
+
fixtureId: value.fixtureId,
|
|
12991
|
+
tenantKey: value.tenantKey,
|
|
12992
|
+
state: "staged",
|
|
12993
|
+
idempotent: true
|
|
12994
|
+
};
|
|
12995
|
+
}
|
|
12996
|
+
async function applicationOriginMatches(http, projectRef2, applicationOrigin) {
|
|
12997
|
+
const endpointRead = await http.get(`${endpoint(projectRef2)}/endpoint/projection`, { maxResponseBytes: PROJECT_ENDPOINT_RESPONSE_MAX_BYTES });
|
|
12998
|
+
return projectApiOrigins(endpointRead, projectRef2)?.includes(applicationOrigin) === true;
|
|
12999
|
+
}
|
|
12964
13000
|
function registerReleaseTools(server, http, options = {}) {
|
|
12965
|
-
server.tool("release", "Verified release controls
|
|
13001
|
+
server.tool("release", "Verified release controls. Management actions use the Management API; release_canary_fixture_stage_replay additionally requires the selected project's SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.", {
|
|
12966
13002
|
action: withDescription(stringEnum([
|
|
12967
13003
|
"logical_backup_list",
|
|
12968
13004
|
"logical_backup_create",
|
|
12969
13005
|
"logical_backup_restore",
|
|
12970
13006
|
"postgrest_status",
|
|
12971
|
-
"postgrest_restart"
|
|
13007
|
+
"postgrest_restart",
|
|
13008
|
+
"release_canary_fixture_stage_replay"
|
|
12972
13009
|
]), "Release control action"),
|
|
12973
13010
|
ref: optional(Type.String(), options.projectRef ? "Optional override when not auto-linked" : "Project ref"),
|
|
12974
13011
|
backup_id: optional(Type.String(), "[logical_backup_restore] Exact verified logical-full backup ID from the selected project inventory"),
|
|
12975
13012
|
expected_sha256: optional(Type.String(), "[logical_backup_restore] Exact lowercase SHA-256 from the selected project inventory"),
|
|
12976
|
-
restore_confirmation: optional(Type.String(), "[logical_backup_restore] Exact RESTORE_PROJECT:<ref>:<backup_id>:<sha256> confirmation")
|
|
12977
|
-
|
|
13013
|
+
restore_confirmation: optional(Type.String(), "[logical_backup_restore] Exact RESTORE_PROJECT:<ref>:<backup_id>:<sha256> confirmation"),
|
|
13014
|
+
subject: optional(Type.String(), "[release_canary_fixture_stage_replay] Exact central subject UUID"),
|
|
13015
|
+
request_id: optional(Type.String(), "[release_canary_fixture_stage_replay] Exact idempotent stage request UUID")
|
|
13016
|
+
}, async ({ action, ref, backup_id, expected_sha256, restore_confirmation, subject, request_id }) => {
|
|
12978
13017
|
const projectRef2 = typeof ref === "string" && ref || options.projectRef;
|
|
12979
13018
|
if (!projectRef2)
|
|
12980
13019
|
throw new Error("'ref' is required for release controls");
|
|
@@ -13045,6 +13084,27 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
13045
13084
|
postgrest: read2.status
|
|
13046
13085
|
});
|
|
13047
13086
|
}
|
|
13087
|
+
if (action === "release_canary_fixture_stage_replay") {
|
|
13088
|
+
if (!options.applicationHttp || !options.applicationOrigin) {
|
|
13089
|
+
throw new Error("release_canary_fixture_stage_replay requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY");
|
|
13090
|
+
}
|
|
13091
|
+
const request = releaseCanaryStageInput(subject, request_id);
|
|
13092
|
+
if (!await applicationOriginMatches(http, projectRef2, options.applicationOrigin)) {
|
|
13093
|
+
return releaseControlFailure("release.release_canary.fixture_stage_replay", "INVALID_RESPONSE", null);
|
|
13094
|
+
}
|
|
13095
|
+
const response = await options.applicationHttp.postReleaseMutation("/rest/v1/rpc/fa_release_canary_fixture_stage", request);
|
|
13096
|
+
if (!response.ok || response.status !== 200) {
|
|
13097
|
+
return mutationFailure("release.release_canary.fixture_stage_replay", response);
|
|
13098
|
+
}
|
|
13099
|
+
const receipt = releaseCanaryStageReceipt(response.data);
|
|
13100
|
+
if (!receipt || !await applicationOriginMatches(http, projectRef2, options.applicationOrigin)) {
|
|
13101
|
+
return releaseControlFailure("release.release_canary.fixture_stage_replay", "OUTCOME_UNKNOWN", response.status);
|
|
13102
|
+
}
|
|
13103
|
+
return releaseControlSuccess("release.release_canary.fixture_stage_replay", {
|
|
13104
|
+
project_ref: projectRef2,
|
|
13105
|
+
receipt
|
|
13106
|
+
});
|
|
13107
|
+
}
|
|
13048
13108
|
if (action !== "postgrest_restart")
|
|
13049
13109
|
throw new Error("Unknown release control action");
|
|
13050
13110
|
const mutation = await http.postReleaseMutation(`${endpoint(projectRef2)}/services/postgrest/restart`);
|
|
@@ -13065,7 +13125,7 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
13065
13125
|
// package.json
|
|
13066
13126
|
var package_default = {
|
|
13067
13127
|
name: "@supacloud/cli",
|
|
13068
|
-
version: "0.
|
|
13128
|
+
version: "0.29.0",
|
|
13069
13129
|
description: "Project-scoped CLI for SupaCloud users",
|
|
13070
13130
|
type: "module",
|
|
13071
13131
|
main: "./dist/index.js",
|
|
@@ -13294,7 +13354,8 @@ DEFAULT CONTEXT
|
|
|
13294
13354
|
Without a selector or project variables, runs use the current project's legacy .env.
|
|
13295
13355
|
Application status accepts SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY.
|
|
13296
13356
|
Management-backed project commands require SUPACLOUD_API_URL +
|
|
13297
|
-
SUPACLOUD_API_TOKEN.
|
|
13357
|
+
SUPACLOUD_API_TOKEN. Only release release_canary_fixture_stage_replay may additionally use
|
|
13358
|
+
the selected project's SUPABASE_* pair for one application-origin RPC.
|
|
13298
13359
|
SUPACLOUD_PROJECT_REF is required when it cannot be inferred from <ref>.api.*.
|
|
13299
13360
|
|
|
13300
13361
|
SUPACLOUD_READ_ONLY=true blocks remote writes. Production writes require an
|
|
@@ -13315,6 +13376,7 @@ EXAMPLES
|
|
|
13315
13376
|
${preferredCommand} release logical_backup_restore --ref abc123 --backup_id <backup_id> --expected_sha256 <sha256> --restore_confirmation RESTORE_PROJECT:abc123:<backup_id>:<sha256>
|
|
13316
13377
|
${preferredCommand} release postgrest_status --ref abc123
|
|
13317
13378
|
${preferredCommand} release postgrest_restart --ref abc123
|
|
13379
|
+
${preferredCommand} release release_canary_fixture_stage_replay --ref abc123 --subject <uuid> --request_id <uuid>
|
|
13318
13380
|
${preferredCommand} queue stats --queue emails
|
|
13319
13381
|
${preferredCommand} queue dlq --queue emails --limit 20
|
|
13320
13382
|
${preferredCommand} frontend list --ref abc123
|
|
@@ -13473,6 +13535,11 @@ function createCliTools(context, confirmProduction) {
|
|
|
13473
13535
|
baseUrl: context.apiUrl,
|
|
13474
13536
|
token: context.apiToken
|
|
13475
13537
|
});
|
|
13538
|
+
const applicationHttp = context.inferredSupabaseUrl && context.inferredServiceRoleKey ? new HttpTransport({
|
|
13539
|
+
baseUrl: context.inferredSupabaseUrl,
|
|
13540
|
+
token: context.inferredServiceRoleKey,
|
|
13541
|
+
apiKey: context.inferredServiceRoleKey
|
|
13542
|
+
}) : undefined;
|
|
13476
13543
|
const assign = (extra) => Object.assign(tools, extra);
|
|
13477
13544
|
assign(captureTools((server) => registerUserProjectCliTools(server, http, {
|
|
13478
13545
|
projectRef: context.projectRef || undefined
|
|
@@ -13494,7 +13561,9 @@ function createCliTools(context, confirmProduction) {
|
|
|
13494
13561
|
})));
|
|
13495
13562
|
assign(captureTools((server) => registerMutationTools(server, http)));
|
|
13496
13563
|
assign(captureTools((server) => registerReleaseTools(server, http, {
|
|
13497
|
-
projectRef: context.projectRef || undefined
|
|
13564
|
+
projectRef: context.projectRef || undefined,
|
|
13565
|
+
applicationHttp,
|
|
13566
|
+
applicationOrigin: context.inferredSupabaseUrl || undefined
|
|
13498
13567
|
})));
|
|
13499
13568
|
assign(captureTools((server) => registerFrontendTools(server, http)));
|
|
13500
13569
|
assign(captureTools((server) => registerGatewayTools(server, http, {
|
package/package.json
CHANGED
|
@@ -15,6 +15,7 @@ Load this reference when selecting a command surface or when a user asks an AI t
|
|
|
15
15
|
| Generate migration from local schema changes | `supabase db_diff` | Review generated SQL before use |
|
|
16
16
|
| Rebuild local database | `supabase db_reset` | Local only; preserve required seed behavior |
|
|
17
17
|
| Inspect or back up a remote database | `supabase db_pull`, `migration_list`, `db_dump`, `gen_types` | Requires explicit PostgreSQL DSN; redact it |
|
|
18
|
+
| 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 |
|
|
18
19
|
| Preview/apply migrations remotely | `supabase push` | Always dry-run first; production needs explicit approval |
|
|
19
20
|
| Mark proven-equivalent historical migrations as applied | `database baseline_migrations` | Dry-run, schema-equivalence proof, backup, explicit approval |
|
|
20
21
|
| 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 |
|
|
@@ -39,6 +40,7 @@ until a project-scoped context is resolved.
|
|
|
39
40
|
- `secrets`: project secret management; never print values after write.
|
|
40
41
|
- `queue`, `task_events`, `diagnostics`: asynchronous workload operations and bounded diagnostics.
|
|
41
42
|
- `gateway`: project route/config/rebuild operations.
|
|
43
|
+
- `release`: verified backup/PostgREST lifecycle controls plus a production-confirmed, non-retried, strict release-canary fixture stage replay. The replay requires both the selected Management project binding and that project's `SUPABASE_URL`/`SUPABASE_SERVICE_ROLE_KEY`.
|
|
42
44
|
- `ai`: inspect or install this packaged Skill.
|
|
43
45
|
|
|
44
46
|
## Safe inspection pattern
|