@supacloud/cli 0.28.1 → 0.30.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 +31 -4
- package/dist/index.js +165 -9
- package/package.json +1 -1
- package/skills/supacloud-cli/references/command-map.md +3 -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,11 @@ 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>
|
|
145
|
+
supacloud-cli release release_canary_fixture_disable_replay --ref abc123 \
|
|
146
|
+
--fixture_id <fixture-uuid> --disable_request_id <disable-request-uuid> \
|
|
147
|
+
--issuer <issuer-url> --subject <central-subject-uuid>
|
|
141
148
|
```
|
|
142
149
|
|
|
143
150
|
Backup creation reports success only after the CLI verifies exactly one new
|
|
@@ -158,6 +165,26 @@ receipt and reads back `desired=running`, `actual=running`, and
|
|
|
158
165
|
`health=healthy`. Both mutating controls follow the normal production
|
|
159
166
|
confirmation and read-only protections.
|
|
160
167
|
|
|
168
|
+
`release_canary_fixture_stage_replay` performs one non-retried, response-bounded
|
|
169
|
+
call to the fixed `fa_release_canary_fixture_stage` PostgREST RPC with both
|
|
170
|
+
bearer and `apikey` service-role headers. It accepts only canonical subject and
|
|
171
|
+
request UUIDs, requires a strict `staged` and `idempotent=true` four-field
|
|
172
|
+
receipt, and emits only that safe projection. Before and after the call, the CLI
|
|
173
|
+
reads the selected project's authoritative endpoint projection and requires the
|
|
174
|
+
configured application origin to match its API origin or alias. Production
|
|
175
|
+
confirmation is mandatory.
|
|
176
|
+
|
|
177
|
+
`release_canary_fixture_disable_replay` performs one non-retried call to the
|
|
178
|
+
fixed `fa_release_canary_fixture_disable` PostgREST RPC using the selected
|
|
179
|
+
project's application service-role origin. Its receipt must contain exactly
|
|
180
|
+
`fixtureId`, `state="disabled"`, and a boolean `idempotent`; both the first
|
|
181
|
+
disable (`idempotent=false`) and same-request replay (`idempotent=true`) are
|
|
182
|
+
valid. The CLI then calls the existing fixed
|
|
183
|
+
`fa_release_canary_fixture_pending` RPC with only the exact issuer and subject
|
|
184
|
+
query parameters and accepts only its authoritative JSON boolean `false`
|
|
185
|
+
read-back. The fixture binding comes from the validated disable receipt before
|
|
186
|
+
reporting success. Management endpoint projection is checked before and after.
|
|
187
|
+
|
|
161
188
|
The legacy `.env` fallback is unclassified and therefore does not enable the
|
|
162
189
|
production confirmation gate. Production automation must select a `prod` or
|
|
163
190
|
`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", "release_canary_fixture_disable_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,13 @@ 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"]);
|
|
12847
|
+
var RELEASE_CANARY_DISABLE_RECEIPT_KEYS = new Set(["fixtureId", "state", "idempotent"]);
|
|
12848
|
+
var RELEASE_CANARY_DISABLE_RPC_PATH = "/rest/v1/rpc/fa_release_canary_fixture_disable";
|
|
12849
|
+
var RELEASE_CANARY_PENDING_RPC_PATH = "/rest/v1/rpc/fa_release_canary_fixture_pending";
|
|
12850
|
+
var RELEASE_CANARY_CLAIM_MAX_LENGTH = 2048;
|
|
12832
12851
|
function isRecord3(value) {
|
|
12833
12852
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12834
12853
|
}
|
|
@@ -12961,20 +12980,96 @@ function readPostgrestFailure(operation, read) {
|
|
|
12961
12980
|
function isRestartReceipt(value) {
|
|
12962
12981
|
return isRecord3(value) && value.service === "postgrest" && value.action === "restart" && value.success === true;
|
|
12963
12982
|
}
|
|
12983
|
+
function releaseCanaryStageInput(subject, requestId) {
|
|
12984
|
+
if (typeof subject !== "string" || !UUID.test(subject))
|
|
12985
|
+
throw new Error("'subject' must be a canonical UUID");
|
|
12986
|
+
if (typeof requestId !== "string" || !UUID.test(requestId))
|
|
12987
|
+
throw new Error("'request_id' must be a canonical UUID");
|
|
12988
|
+
return { p_subject: subject, p_request_id: requestId };
|
|
12989
|
+
}
|
|
12990
|
+
function releaseCanaryStageReceipt(value) {
|
|
12991
|
+
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)
|
|
12992
|
+
return null;
|
|
12993
|
+
return {
|
|
12994
|
+
fixtureId: value.fixtureId,
|
|
12995
|
+
tenantKey: value.tenantKey,
|
|
12996
|
+
state: "staged",
|
|
12997
|
+
idempotent: true
|
|
12998
|
+
};
|
|
12999
|
+
}
|
|
13000
|
+
function releaseCanaryIssuer(value) {
|
|
13001
|
+
if (typeof value !== "string" || value.length === 0 || value.length > RELEASE_CANARY_CLAIM_MAX_LENGTH || value !== value.trim() || /[\u0000-\u001f\u007f]/u.test(value)) {
|
|
13002
|
+
throw new Error("'issuer' must be a bounded absolute HTTP(S) issuer");
|
|
13003
|
+
}
|
|
13004
|
+
let parsed;
|
|
13005
|
+
try {
|
|
13006
|
+
parsed = new URL(value);
|
|
13007
|
+
} catch {
|
|
13008
|
+
throw new Error("'issuer' must be a bounded absolute HTTP(S) issuer");
|
|
13009
|
+
}
|
|
13010
|
+
const loopbackHttp = parsed.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(parsed.hostname) && parsed.port.length > 0;
|
|
13011
|
+
if (parsed.protocol !== "https:" && !loopbackHttp || parsed.username || parsed.password || parsed.search || parsed.hash) {
|
|
13012
|
+
throw new Error("'issuer' must be a bounded absolute HTTP(S) issuer");
|
|
13013
|
+
}
|
|
13014
|
+
return value;
|
|
13015
|
+
}
|
|
13016
|
+
function releaseCanaryDisableInput(fixtureId, disableRequestId, issuer, subject) {
|
|
13017
|
+
if (typeof fixtureId !== "string" || !UUID.test(fixtureId)) {
|
|
13018
|
+
throw new Error("'fixture_id' must be a canonical UUID");
|
|
13019
|
+
}
|
|
13020
|
+
if (typeof disableRequestId !== "string" || !UUID.test(disableRequestId)) {
|
|
13021
|
+
throw new Error("'disable_request_id' must be a canonical UUID");
|
|
13022
|
+
}
|
|
13023
|
+
if (typeof subject !== "string" || !UUID.test(subject)) {
|
|
13024
|
+
throw new Error("'subject' must be a canonical UUID");
|
|
13025
|
+
}
|
|
13026
|
+
return {
|
|
13027
|
+
p_fixture_id: fixtureId,
|
|
13028
|
+
p_disable_request_id: disableRequestId,
|
|
13029
|
+
p_issuer: releaseCanaryIssuer(issuer),
|
|
13030
|
+
p_subject: subject
|
|
13031
|
+
};
|
|
13032
|
+
}
|
|
13033
|
+
function releaseCanaryDisableReceipt(receiptCandidate, fixtureId) {
|
|
13034
|
+
if (!isRecord3(receiptCandidate) || Object.keys(receiptCandidate).some((key) => !RELEASE_CANARY_DISABLE_RECEIPT_KEYS.has(key)) || Object.keys(receiptCandidate).length !== RELEASE_CANARY_DISABLE_RECEIPT_KEYS.size || receiptCandidate.fixtureId !== fixtureId || receiptCandidate.state !== "disabled" || typeof receiptCandidate.idempotent !== "boolean")
|
|
13035
|
+
return null;
|
|
13036
|
+
return { fixtureId, state: "disabled", idempotent: receiptCandidate.idempotent };
|
|
13037
|
+
}
|
|
13038
|
+
function releaseCanaryPendingPath(request) {
|
|
13039
|
+
const params = new URLSearchParams({
|
|
13040
|
+
p_issuer: request.p_issuer,
|
|
13041
|
+
p_subject: request.p_subject
|
|
13042
|
+
});
|
|
13043
|
+
return `${RELEASE_CANARY_PENDING_RPC_PATH}?${params.toString()}`;
|
|
13044
|
+
}
|
|
13045
|
+
function releaseCanaryPendingReadback(readbackCandidate) {
|
|
13046
|
+
return readbackCandidate === false;
|
|
13047
|
+
}
|
|
13048
|
+
async function applicationOriginMatches(http, projectRef2, applicationOrigin) {
|
|
13049
|
+
const endpointRead = await http.get(`${endpoint(projectRef2)}/endpoint/projection`, { maxJsonBytes: PROJECT_ENDPOINT_RESPONSE_MAX_BYTES, responseTimeoutMs: RELEASE_READ_RESPONSE_TIMEOUT_MS });
|
|
13050
|
+
return projectApiOrigins(endpointRead, projectRef2)?.includes(applicationOrigin) === true;
|
|
13051
|
+
}
|
|
12964
13052
|
function registerReleaseTools(server, http, options = {}) {
|
|
12965
|
-
server.tool("release", "Verified release controls
|
|
13053
|
+
server.tool("release", "Verified release controls. Management actions use the Management API; release canary stage/disable replay additionally require the selected project's SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.", {
|
|
12966
13054
|
action: withDescription(stringEnum([
|
|
12967
13055
|
"logical_backup_list",
|
|
12968
13056
|
"logical_backup_create",
|
|
12969
13057
|
"logical_backup_restore",
|
|
12970
13058
|
"postgrest_status",
|
|
12971
|
-
"postgrest_restart"
|
|
13059
|
+
"postgrest_restart",
|
|
13060
|
+
"release_canary_fixture_stage_replay",
|
|
13061
|
+
"release_canary_fixture_disable_replay"
|
|
12972
13062
|
]), "Release control action"),
|
|
12973
13063
|
ref: optional(Type.String(), options.projectRef ? "Optional override when not auto-linked" : "Project ref"),
|
|
12974
13064
|
backup_id: optional(Type.String(), "[logical_backup_restore] Exact verified logical-full backup ID from the selected project inventory"),
|
|
12975
13065
|
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
|
-
|
|
13066
|
+
restore_confirmation: optional(Type.String(), "[logical_backup_restore] Exact RESTORE_PROJECT:<ref>:<backup_id>:<sha256> confirmation"),
|
|
13067
|
+
subject: optional(Type.String(), "[release_canary_fixture_stage_replay/disable_replay] Exact central subject UUID"),
|
|
13068
|
+
request_id: optional(Type.String(), "[release_canary_fixture_stage_replay] Exact idempotent stage request UUID"),
|
|
13069
|
+
fixture_id: optional(Type.String(), "[release_canary_fixture_disable_replay] Exact staged fixture UUID"),
|
|
13070
|
+
disable_request_id: optional(Type.String(), "[release_canary_fixture_disable_replay] Exact idempotent disable request UUID"),
|
|
13071
|
+
issuer: optional(Type.String(), "[release_canary_fixture_disable_replay] Exact HTTP(S) issuer")
|
|
13072
|
+
}, async ({ action, ref, backup_id, expected_sha256, restore_confirmation, subject, request_id, fixture_id, disable_request_id, issuer }) => {
|
|
12978
13073
|
const projectRef2 = typeof ref === "string" && ref || options.projectRef;
|
|
12979
13074
|
if (!projectRef2)
|
|
12980
13075
|
throw new Error("'ref' is required for release controls");
|
|
@@ -13045,6 +13140,57 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
13045
13140
|
postgrest: read2.status
|
|
13046
13141
|
});
|
|
13047
13142
|
}
|
|
13143
|
+
if (action === "release_canary_fixture_stage_replay") {
|
|
13144
|
+
if (!options.applicationHttp || !options.applicationOrigin) {
|
|
13145
|
+
throw new Error("release_canary_fixture_stage_replay requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY");
|
|
13146
|
+
}
|
|
13147
|
+
const request = releaseCanaryStageInput(subject, request_id);
|
|
13148
|
+
if (!await applicationOriginMatches(http, projectRef2, options.applicationOrigin)) {
|
|
13149
|
+
return releaseControlFailure("release.release_canary.fixture_stage_replay", "INVALID_RESPONSE", null);
|
|
13150
|
+
}
|
|
13151
|
+
const response = await options.applicationHttp.postReleaseMutation("/rest/v1/rpc/fa_release_canary_fixture_stage", request);
|
|
13152
|
+
if (!response.ok || response.status !== 200) {
|
|
13153
|
+
return mutationFailure("release.release_canary.fixture_stage_replay", response);
|
|
13154
|
+
}
|
|
13155
|
+
const receipt = releaseCanaryStageReceipt(response.data);
|
|
13156
|
+
if (!receipt || !await applicationOriginMatches(http, projectRef2, options.applicationOrigin)) {
|
|
13157
|
+
return releaseControlFailure("release.release_canary.fixture_stage_replay", "OUTCOME_UNKNOWN", response.status);
|
|
13158
|
+
}
|
|
13159
|
+
return releaseControlSuccess("release.release_canary.fixture_stage_replay", {
|
|
13160
|
+
project_ref: projectRef2,
|
|
13161
|
+
receipt
|
|
13162
|
+
});
|
|
13163
|
+
}
|
|
13164
|
+
if (action === "release_canary_fixture_disable_replay") {
|
|
13165
|
+
if (!options.applicationHttp || !options.applicationOrigin) {
|
|
13166
|
+
throw new Error("release_canary_fixture_disable_replay requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY");
|
|
13167
|
+
}
|
|
13168
|
+
const request = releaseCanaryDisableInput(fixture_id, disable_request_id, issuer, subject);
|
|
13169
|
+
if (!await applicationOriginMatches(http, projectRef2, options.applicationOrigin)) {
|
|
13170
|
+
return releaseControlFailure("release.release_canary.fixture_disable_replay", "INVALID_RESPONSE", null);
|
|
13171
|
+
}
|
|
13172
|
+
const response = await options.applicationHttp.postReleaseMutation(RELEASE_CANARY_DISABLE_RPC_PATH, request);
|
|
13173
|
+
if (!response.ok || response.status !== 200) {
|
|
13174
|
+
return mutationFailure("release.release_canary.fixture_disable_replay", response);
|
|
13175
|
+
}
|
|
13176
|
+
const receipt = releaseCanaryDisableReceipt(response.data, request.p_fixture_id);
|
|
13177
|
+
if (!receipt) {
|
|
13178
|
+
return releaseControlFailure("release.release_canary.fixture_disable_replay", "OUTCOME_UNKNOWN", response.status);
|
|
13179
|
+
}
|
|
13180
|
+
const pendingRequest = {
|
|
13181
|
+
p_issuer: request.p_issuer,
|
|
13182
|
+
p_subject: request.p_subject
|
|
13183
|
+
};
|
|
13184
|
+
const pendingResponse = await options.applicationHttp.get(releaseCanaryPendingPath(pendingRequest), { maxJsonBytes: MUTATION_MAX_BYTES, responseTimeoutMs: RELEASE_READ_RESPONSE_TIMEOUT_MS });
|
|
13185
|
+
if (!pendingResponse.ok || pendingResponse.status !== 200 || !releaseCanaryPendingReadback(pendingResponse.data) || !await applicationOriginMatches(http, projectRef2, options.applicationOrigin)) {
|
|
13186
|
+
return releaseControlFailure("release.release_canary.fixture_disable_replay", "OUTCOME_UNKNOWN", pendingResponse.transportError ? null : pendingResponse.status);
|
|
13187
|
+
}
|
|
13188
|
+
return releaseControlSuccess("release.release_canary.fixture_disable_replay", {
|
|
13189
|
+
project_ref: projectRef2,
|
|
13190
|
+
receipt,
|
|
13191
|
+
pending: false
|
|
13192
|
+
});
|
|
13193
|
+
}
|
|
13048
13194
|
if (action !== "postgrest_restart")
|
|
13049
13195
|
throw new Error("Unknown release control action");
|
|
13050
13196
|
const mutation = await http.postReleaseMutation(`${endpoint(projectRef2)}/services/postgrest/restart`);
|
|
@@ -13065,7 +13211,7 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
13065
13211
|
// package.json
|
|
13066
13212
|
var package_default = {
|
|
13067
13213
|
name: "@supacloud/cli",
|
|
13068
|
-
version: "0.
|
|
13214
|
+
version: "0.30.0",
|
|
13069
13215
|
description: "Project-scoped CLI for SupaCloud users",
|
|
13070
13216
|
type: "module",
|
|
13071
13217
|
main: "./dist/index.js",
|
|
@@ -13294,7 +13440,8 @@ DEFAULT CONTEXT
|
|
|
13294
13440
|
Without a selector or project variables, runs use the current project's legacy .env.
|
|
13295
13441
|
Application status accepts SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY.
|
|
13296
13442
|
Management-backed project commands require SUPACLOUD_API_URL +
|
|
13297
|
-
SUPACLOUD_API_TOKEN.
|
|
13443
|
+
SUPACLOUD_API_TOKEN. Only release canary stage/disable replay actions may additionally use
|
|
13444
|
+
the selected project's SUPABASE_* pair for fixed application-origin RPCs.
|
|
13298
13445
|
SUPACLOUD_PROJECT_REF is required when it cannot be inferred from <ref>.api.*.
|
|
13299
13446
|
|
|
13300
13447
|
SUPACLOUD_READ_ONLY=true blocks remote writes. Production writes require an
|
|
@@ -13315,6 +13462,8 @@ EXAMPLES
|
|
|
13315
13462
|
${preferredCommand} release logical_backup_restore --ref abc123 --backup_id <backup_id> --expected_sha256 <sha256> --restore_confirmation RESTORE_PROJECT:abc123:<backup_id>:<sha256>
|
|
13316
13463
|
${preferredCommand} release postgrest_status --ref abc123
|
|
13317
13464
|
${preferredCommand} release postgrest_restart --ref abc123
|
|
13465
|
+
${preferredCommand} release release_canary_fixture_stage_replay --ref abc123 --subject <uuid> --request_id <uuid>
|
|
13466
|
+
${preferredCommand} release release_canary_fixture_disable_replay --ref abc123 --fixture_id <uuid> --disable_request_id <uuid> --issuer <issuer-url> --subject <uuid>
|
|
13318
13467
|
${preferredCommand} queue stats --queue emails
|
|
13319
13468
|
${preferredCommand} queue dlq --queue emails --limit 20
|
|
13320
13469
|
${preferredCommand} frontend list --ref abc123
|
|
@@ -13473,6 +13622,11 @@ function createCliTools(context, confirmProduction) {
|
|
|
13473
13622
|
baseUrl: context.apiUrl,
|
|
13474
13623
|
token: context.apiToken
|
|
13475
13624
|
});
|
|
13625
|
+
const applicationHttp = context.inferredSupabaseUrl && context.inferredServiceRoleKey ? new HttpTransport({
|
|
13626
|
+
baseUrl: context.inferredSupabaseUrl,
|
|
13627
|
+
token: context.inferredServiceRoleKey,
|
|
13628
|
+
apiKey: context.inferredServiceRoleKey
|
|
13629
|
+
}) : undefined;
|
|
13476
13630
|
const assign = (extra) => Object.assign(tools, extra);
|
|
13477
13631
|
assign(captureTools((server) => registerUserProjectCliTools(server, http, {
|
|
13478
13632
|
projectRef: context.projectRef || undefined
|
|
@@ -13494,7 +13648,9 @@ function createCliTools(context, confirmProduction) {
|
|
|
13494
13648
|
})));
|
|
13495
13649
|
assign(captureTools((server) => registerMutationTools(server, http)));
|
|
13496
13650
|
assign(captureTools((server) => registerReleaseTools(server, http, {
|
|
13497
|
-
projectRef: context.projectRef || undefined
|
|
13651
|
+
projectRef: context.projectRef || undefined,
|
|
13652
|
+
applicationHttp,
|
|
13653
|
+
applicationOrigin: context.inferredSupabaseUrl || undefined
|
|
13498
13654
|
})));
|
|
13499
13655
|
assign(captureTools((server) => registerFrontendTools(server, http)));
|
|
13500
13656
|
assign(captureTools((server) => registerGatewayTools(server, http, {
|
package/package.json
CHANGED
|
@@ -15,6 +15,8 @@ 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 |
|
|
19
|
+
| 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 |
|
|
18
20
|
| Preview/apply migrations remotely | `supabase push` | Always dry-run first; production needs explicit approval |
|
|
19
21
|
| Mark proven-equivalent historical migrations as applied | `database baseline_migrations` | Dry-run, schema-equivalence proof, backup, explicit approval |
|
|
20
22
|
| 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 +41,7 @@ until a project-scoped context is resolved.
|
|
|
39
41
|
- `secrets`: project secret management; never print values after write.
|
|
40
42
|
- `queue`, `task_events`, `diagnostics`: asynchronous workload operations and bounded diagnostics.
|
|
41
43
|
- `gateway`: project route/config/rebuild operations.
|
|
44
|
+
- `release`: verified backup/PostgREST lifecycle controls plus production-confirmed, non-retried, strict release-canary fixture stage and disable replay actions. These actions require both the selected Management project binding and that project's `SUPABASE_URL`/`SUPABASE_SERVICE_ROLE_KEY`.
|
|
42
45
|
- `ai`: inspect or install this packaged Skill.
|
|
43
46
|
|
|
44
47
|
## Safe inspection pattern
|