@supacloud/cli 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -6471,8 +6471,8 @@ var ACTION_POLICY = {
|
|
|
6471
6471
|
write: ["push"]
|
|
6472
6472
|
},
|
|
6473
6473
|
auth: {
|
|
6474
|
-
read: ["list_providers", "get_provider", "supported_providers", "get_settings", "get_config"],
|
|
6475
|
-
write: ["configure_provider", "update_provider", "disable_provider", "wechat_mini", "wechat_open", "update_settings", "update_config"]
|
|
6474
|
+
read: ["list_users", "get_user", "list_providers", "get_provider", "supported_providers", "get_settings", "get_config"],
|
|
6475
|
+
write: ["generate_link", "configure_provider", "update_provider", "disable_provider", "wechat_mini", "wechat_open", "update_settings", "update_config"]
|
|
6476
6476
|
},
|
|
6477
6477
|
oauth_clients: {
|
|
6478
6478
|
read: ["list", "get"],
|
|
@@ -7968,6 +7968,25 @@ var safeAuthMutationCodes = new Set([
|
|
|
7968
7968
|
"AUTH_RUNTIME_APPLY_FAILED",
|
|
7969
7969
|
"SUPAUTH_DEPENDENT_REFRESH_FAILED"
|
|
7970
7970
|
]);
|
|
7971
|
+
var MAX_AUTH_READ_BYTES = 64 * 1024;
|
|
7972
|
+
var AUTH_READ_TIMEOUT_MS = 5000;
|
|
7973
|
+
var USER_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
7974
|
+
var AUTH_LINK_TYPES = [
|
|
7975
|
+
"signup",
|
|
7976
|
+
"magiclink",
|
|
7977
|
+
"recovery",
|
|
7978
|
+
"invite",
|
|
7979
|
+
"email_change",
|
|
7980
|
+
"email_change_current",
|
|
7981
|
+
"email_change_new"
|
|
7982
|
+
];
|
|
7983
|
+
var SAFE_USER_FIELDS = [
|
|
7984
|
+
"id",
|
|
7985
|
+
"email",
|
|
7986
|
+
"phone",
|
|
7987
|
+
"created_at",
|
|
7988
|
+
"last_sign_in_at"
|
|
7989
|
+
];
|
|
7971
7990
|
function parseAuthConfig(input) {
|
|
7972
7991
|
if (typeof input !== "string")
|
|
7973
7992
|
return input;
|
|
@@ -8019,6 +8038,162 @@ function authMutationResult(response, successMessage) {
|
|
|
8019
8038
|
}]
|
|
8020
8039
|
};
|
|
8021
8040
|
}
|
|
8041
|
+
function requiredRef(candidate) {
|
|
8042
|
+
if (typeof candidate !== "string" || !candidate.trim())
|
|
8043
|
+
throw new Error("'ref' is required");
|
|
8044
|
+
return projectRefPathSegment(candidate.trim(), "Auth");
|
|
8045
|
+
}
|
|
8046
|
+
function requiredUserId(candidate) {
|
|
8047
|
+
if (typeof candidate !== "string" || !USER_ID_PATTERN.test(candidate.trim())) {
|
|
8048
|
+
throw new Error("'user_id' must be a UUID");
|
|
8049
|
+
}
|
|
8050
|
+
return candidate.trim().toLowerCase();
|
|
8051
|
+
}
|
|
8052
|
+
function boundedPage(candidate) {
|
|
8053
|
+
if (candidate === undefined)
|
|
8054
|
+
return 1;
|
|
8055
|
+
if (typeof candidate !== "number" || !Number.isSafeInteger(candidate) || candidate < 1) {
|
|
8056
|
+
throw new Error("'page' must be a positive integer");
|
|
8057
|
+
}
|
|
8058
|
+
return candidate;
|
|
8059
|
+
}
|
|
8060
|
+
function boundedPerPage(candidate) {
|
|
8061
|
+
if (candidate === undefined)
|
|
8062
|
+
return 50;
|
|
8063
|
+
if (typeof candidate !== "number" || !Number.isSafeInteger(candidate) || candidate < 1 || candidate > 100) {
|
|
8064
|
+
throw new Error("'per_page' must be an integer between 1 and 100");
|
|
8065
|
+
}
|
|
8066
|
+
return candidate;
|
|
8067
|
+
}
|
|
8068
|
+
function safeRedirectTo(candidate) {
|
|
8069
|
+
if (candidate === undefined)
|
|
8070
|
+
return;
|
|
8071
|
+
if (typeof candidate !== "string" || !candidate.trim())
|
|
8072
|
+
throw new Error("'redirect_to' must be an absolute HTTPS or loopback HTTP URL");
|
|
8073
|
+
let uri;
|
|
8074
|
+
try {
|
|
8075
|
+
uri = new URL(candidate.trim());
|
|
8076
|
+
} catch {
|
|
8077
|
+
throw new Error("'redirect_to' must be an absolute HTTPS or loopback HTTP URL");
|
|
8078
|
+
}
|
|
8079
|
+
const loopback = uri.hostname === "127.0.0.1" || uri.hostname === "[::1]";
|
|
8080
|
+
const validProtocol = uri.protocol === "https:" || uri.protocol === "http:" && loopback && Boolean(uri.port);
|
|
8081
|
+
if (!validProtocol || uri.username || uri.password || uri.hash) {
|
|
8082
|
+
throw new Error("'redirect_to' must be an absolute HTTPS or loopback HTTP URL without credentials or fragment");
|
|
8083
|
+
}
|
|
8084
|
+
return uri.toString();
|
|
8085
|
+
}
|
|
8086
|
+
function isRecord(candidate) {
|
|
8087
|
+
return candidate !== null && typeof candidate === "object" && !Array.isArray(candidate);
|
|
8088
|
+
}
|
|
8089
|
+
function projectUser(candidate) {
|
|
8090
|
+
if (!isRecord(candidate) || typeof candidate.id !== "string")
|
|
8091
|
+
return null;
|
|
8092
|
+
const projectedUser = {};
|
|
8093
|
+
for (const field of SAFE_USER_FIELDS) {
|
|
8094
|
+
if (field in candidate)
|
|
8095
|
+
projectedUser[field] = candidate[field];
|
|
8096
|
+
}
|
|
8097
|
+
return projectedUser;
|
|
8098
|
+
}
|
|
8099
|
+
function projectUserList(candidate) {
|
|
8100
|
+
if (!isRecord(candidate) || !Array.isArray(candidate.users))
|
|
8101
|
+
return null;
|
|
8102
|
+
const users = candidate.users.map(projectUser);
|
|
8103
|
+
if (users.some((user) => user === null))
|
|
8104
|
+
return null;
|
|
8105
|
+
const projectedFields = { users };
|
|
8106
|
+
for (const field of ["total", "page", "per_page", "next_page", "last_page"]) {
|
|
8107
|
+
if (field in candidate && (typeof candidate[field] === "number" || candidate[field] === null)) {
|
|
8108
|
+
projectedFields[field] = candidate[field];
|
|
8109
|
+
}
|
|
8110
|
+
}
|
|
8111
|
+
return projectedFields;
|
|
8112
|
+
}
|
|
8113
|
+
function actionLink(candidate) {
|
|
8114
|
+
const candidates = [candidate];
|
|
8115
|
+
if (isRecord(candidate)) {
|
|
8116
|
+
candidates.push(candidate.data, candidate.properties);
|
|
8117
|
+
if (isRecord(candidate.data))
|
|
8118
|
+
candidates.push(candidate.data.properties);
|
|
8119
|
+
}
|
|
8120
|
+
for (const candidate2 of candidates) {
|
|
8121
|
+
if (isRecord(candidate2) && typeof candidate2.action_link === "string" && candidate2.action_link.length > 0) {
|
|
8122
|
+
return candidate2.action_link;
|
|
8123
|
+
}
|
|
8124
|
+
}
|
|
8125
|
+
return null;
|
|
8126
|
+
}
|
|
8127
|
+
function safeAuthReadFailure(operation, response) {
|
|
8128
|
+
return {
|
|
8129
|
+
isError: true,
|
|
8130
|
+
content: [{
|
|
8131
|
+
type: "text",
|
|
8132
|
+
text: JSON.stringify({
|
|
8133
|
+
ok: false,
|
|
8134
|
+
operation,
|
|
8135
|
+
http_status: response.transportError || response.responseReadError ? null : response.status,
|
|
8136
|
+
error: response.responseReadError ? "INVALID_RESPONSE" : response.transportError ? "NETWORK_ERROR" : "HTTP_ERROR"
|
|
8137
|
+
}, null, 2)
|
|
8138
|
+
}]
|
|
8139
|
+
};
|
|
8140
|
+
}
|
|
8141
|
+
async function listUsers(http, args) {
|
|
8142
|
+
const ref = requiredRef(args.ref);
|
|
8143
|
+
const page = boundedPage(args.page);
|
|
8144
|
+
const perPage = boundedPerPage(args.per_page);
|
|
8145
|
+
const params = new URLSearchParams({ page: String(page), per_page: String(perPage) });
|
|
8146
|
+
for (const key of ["search", "email_like"]) {
|
|
8147
|
+
if (args[key] !== undefined) {
|
|
8148
|
+
if (typeof args[key] !== "string" || !args[key].trim())
|
|
8149
|
+
throw new Error(`'${key}' must be a non-empty string`);
|
|
8150
|
+
params.set(key, args[key].trim());
|
|
8151
|
+
}
|
|
8152
|
+
}
|
|
8153
|
+
const response = await http.get(`/v1/projects/${ref}/auth/users?${params.toString()}`, {
|
|
8154
|
+
maxJsonBytes: MAX_AUTH_READ_BYTES,
|
|
8155
|
+
responseTimeoutMs: AUTH_READ_TIMEOUT_MS
|
|
8156
|
+
});
|
|
8157
|
+
if (!response.ok)
|
|
8158
|
+
return safeAuthReadFailure("auth.list_users", response);
|
|
8159
|
+
const users = projectUserList(response.data);
|
|
8160
|
+
if (!users)
|
|
8161
|
+
return safeAuthReadFailure("auth.list_users", { ...response, responseReadError: true });
|
|
8162
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: true, operation: "auth.list_users", project_ref: ref, ...users }, null, 2) }] };
|
|
8163
|
+
}
|
|
8164
|
+
async function getUser(http, args) {
|
|
8165
|
+
const ref = requiredRef(args.ref);
|
|
8166
|
+
const userId = requiredUserId(args.user_id);
|
|
8167
|
+
const response = await http.get(`/v1/projects/${ref}/auth/users/${encodeURIComponent(userId)}`, {
|
|
8168
|
+
maxJsonBytes: MAX_AUTH_READ_BYTES,
|
|
8169
|
+
responseTimeoutMs: AUTH_READ_TIMEOUT_MS
|
|
8170
|
+
});
|
|
8171
|
+
if (!response.ok)
|
|
8172
|
+
return safeAuthReadFailure("auth.get_user", response);
|
|
8173
|
+
const user = projectUser(response.data);
|
|
8174
|
+
if (!user)
|
|
8175
|
+
return safeAuthReadFailure("auth.get_user", { ...response, responseReadError: true });
|
|
8176
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: true, operation: "auth.get_user", project_ref: ref, user }, null, 2) }] };
|
|
8177
|
+
}
|
|
8178
|
+
async function generateLink(http, args) {
|
|
8179
|
+
const ref = requiredRef(args.ref);
|
|
8180
|
+
if (typeof args.type !== "string" || !AUTH_LINK_TYPES.includes(args.type)) {
|
|
8181
|
+
throw new Error("'type' is invalid for 'generate_link'");
|
|
8182
|
+
}
|
|
8183
|
+
if (typeof args.email !== "string" || !args.email.trim())
|
|
8184
|
+
throw new Error("'email' is required for 'generate_link'");
|
|
8185
|
+
const body = { type: args.type, email: args.email.trim() };
|
|
8186
|
+
const redirectTo = safeRedirectTo(args.redirect_to);
|
|
8187
|
+
if (redirectTo)
|
|
8188
|
+
body.redirect_to = redirectTo;
|
|
8189
|
+
const response = await http.postReleaseMutation(`/v1/projects/${ref}/auth/generate_link`, body);
|
|
8190
|
+
if (!response.ok)
|
|
8191
|
+
return safeAuthReadFailure("auth.generate_link", response);
|
|
8192
|
+
const link = actionLink(response.data);
|
|
8193
|
+
if (!link)
|
|
8194
|
+
return safeAuthReadFailure("auth.generate_link", { ...response, responseReadError: true });
|
|
8195
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: true, operation: "auth.generate_link", action_link: link }, null, 2) }] };
|
|
8196
|
+
}
|
|
8022
8197
|
function formatProviders(data) {
|
|
8023
8198
|
if (!data || typeof data !== "object")
|
|
8024
8199
|
return JSON.stringify(data, null, 2);
|
|
@@ -8051,9 +8226,12 @@ function formatProviders(data) {
|
|
|
8051
8226
|
return out;
|
|
8052
8227
|
}
|
|
8053
8228
|
function registerAuthTools(server, http) {
|
|
8054
|
-
server.tool("auth", `Auth & OAuth provider management.
|
|
8055
|
-
Actions: list_providers, get_provider, configure_provider, update_provider, disable_provider, supported_providers, wechat_mini, wechat_open, get_settings, update_settings, get_config, update_config`, {
|
|
8229
|
+
server.tool("auth", `Auth & OAuth provider management, controlled user lookup, and login-link generation.
|
|
8230
|
+
Actions: list_users, get_user, generate_link, list_providers, get_provider, configure_provider, update_provider, disable_provider, supported_providers, wechat_mini, wechat_open, get_settings, update_settings, get_config, update_config`, {
|
|
8056
8231
|
action: withDescription(stringEnum([
|
|
8232
|
+
"list_users",
|
|
8233
|
+
"get_user",
|
|
8234
|
+
"generate_link",
|
|
8057
8235
|
"list_providers",
|
|
8058
8236
|
"get_provider",
|
|
8059
8237
|
"configure_provider",
|
|
@@ -8068,6 +8246,14 @@ Actions: list_providers, get_provider, configure_provider, update_provider, disa
|
|
|
8068
8246
|
"update_config"
|
|
8069
8247
|
]), "Action to perform"),
|
|
8070
8248
|
ref: optional(Type.String(), "Project ref (required for most actions)"),
|
|
8249
|
+
user_id: optional(Type.String(), "[get_user] Exact auth user UUID"),
|
|
8250
|
+
page: optional(Type.Integer({ minimum: 1 }), "[list_users] 1-based page"),
|
|
8251
|
+
per_page: optional(Type.Integer({ minimum: 1, maximum: 100 }), "[list_users] Users per page (1-100)"),
|
|
8252
|
+
search: optional(Type.String(), "[list_users] Search user email, phone, or UUID"),
|
|
8253
|
+
email_like: optional(Type.String(), "[list_users] Search user email or phone"),
|
|
8254
|
+
type: optional(stringEnum(AUTH_LINK_TYPES), "[generate_link] GoTrue link type"),
|
|
8255
|
+
email: optional(Type.String(), "[generate_link] User email"),
|
|
8256
|
+
redirect_to: optional(Type.String(), "[generate_link] Absolute HTTPS or loopback callback"),
|
|
8071
8257
|
provider: optional(Type.String(), "[*_provider] Provider name (github, google, wechat, etc.)"),
|
|
8072
8258
|
client_id: optional(Type.String(), "[configure/update] OAuth Client ID"),
|
|
8073
8259
|
client_secret: optional(Type.String(), "[configure/update] OAuth Client Secret"),
|
|
@@ -8085,6 +8271,12 @@ Actions: list_providers, get_provider, configure_provider, update_provider, disa
|
|
|
8085
8271
|
const ok = (res) => res.ok ? JSON.stringify(res.data, null, 2) : `❌ Failed (${res.status}): ${JSON.stringify(res.data)}`;
|
|
8086
8272
|
let text;
|
|
8087
8273
|
switch (action) {
|
|
8274
|
+
case "list_users":
|
|
8275
|
+
return listUsers(http, args);
|
|
8276
|
+
case "get_user":
|
|
8277
|
+
return getUser(http, args);
|
|
8278
|
+
case "generate_link":
|
|
8279
|
+
return generateLink(http, args);
|
|
8088
8280
|
case "list_providers":
|
|
8089
8281
|
need("ref");
|
|
8090
8282
|
const lp = await http.get(`/v1/projects/${ref}/auth/providers`);
|
|
@@ -8180,7 +8372,7 @@ var RELEASE_CANARY_CLIENT_NAME = "supacloud-release-canary";
|
|
|
8180
8372
|
var CLIENT_ID_PATTERN = /^[A-Za-z0-9._~-]{1,256}$/;
|
|
8181
8373
|
var MAX_CLIENT_LIST_BYTES = 256 * 1024;
|
|
8182
8374
|
var READ_TIMEOUT_MS = 5000;
|
|
8183
|
-
function
|
|
8375
|
+
function isRecord2(value) {
|
|
8184
8376
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
8185
8377
|
}
|
|
8186
8378
|
function releaseCanaryCallbackUri(value) {
|
|
@@ -8202,7 +8394,7 @@ function releaseCanaryCallbackUri(value) {
|
|
|
8202
8394
|
return uri.toString();
|
|
8203
8395
|
}
|
|
8204
8396
|
function createdClientId(value) {
|
|
8205
|
-
if (!
|
|
8397
|
+
if (!isRecord2(value))
|
|
8206
8398
|
return null;
|
|
8207
8399
|
try {
|
|
8208
8400
|
return clientId(value.client_id);
|
|
@@ -8225,7 +8417,7 @@ function oauthClientsPath(ref) {
|
|
|
8225
8417
|
return `/v1/projects/${encodeURIComponent(ref)}/auth/oauth-clients`;
|
|
8226
8418
|
}
|
|
8227
8419
|
function expectedClient(value, redirectUri) {
|
|
8228
|
-
if (!
|
|
8420
|
+
if (!isRecord2(value) || typeof value.client_id !== "string" || !CLIENT_ID_PATTERN.test(value.client_id) || value.client_name !== RELEASE_CANARY_CLIENT_NAME || value.client_type !== "public" || value.token_endpoint_auth_method !== "none" || !Array.isArray(value.redirect_uris) || value.redirect_uris.length !== 1 || !Array.isArray(value.grant_types) || value.grant_types.length !== 1 || value.grant_types[0] !== "authorization_code" || !Array.isArray(value.response_types) || value.response_types.length !== 1 || value.response_types[0] !== "code")
|
|
8229
8421
|
return null;
|
|
8230
8422
|
let callback;
|
|
8231
8423
|
try {
|
|
@@ -8246,9 +8438,9 @@ function expectedClient(value, redirectUri) {
|
|
|
8246
8438
|
};
|
|
8247
8439
|
}
|
|
8248
8440
|
function clientInventory(value) {
|
|
8249
|
-
if (!
|
|
8441
|
+
if (!isRecord2(value) || !Array.isArray(value.clients))
|
|
8250
8442
|
return null;
|
|
8251
|
-
const clients = value.clients.filter((client) =>
|
|
8443
|
+
const clients = value.clients.filter((client) => isRecord2(client) && client.client_name === RELEASE_CANARY_CLIENT_NAME).map((client) => expectedClient(client));
|
|
8252
8444
|
if (clients.some((client) => client === null))
|
|
8253
8445
|
return null;
|
|
8254
8446
|
const inventory = clients;
|
|
@@ -12567,7 +12759,7 @@ var INVENTORY_MAX_BYTES = 1024 * 1024;
|
|
|
12567
12759
|
var MUTATION_MAX_BYTES = 64 * 1024;
|
|
12568
12760
|
var BACKUP_TIMEOUT_MS = 36 * 60000;
|
|
12569
12761
|
var RELEASE_READ_RESPONSE_TIMEOUT_MS = 5000;
|
|
12570
|
-
function
|
|
12762
|
+
function isRecord3(value) {
|
|
12571
12763
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12572
12764
|
}
|
|
12573
12765
|
function canonicalTimestamp3(value) {
|
|
@@ -12583,7 +12775,7 @@ function backupBelongsToProject(backupId, projectRef2) {
|
|
|
12583
12775
|
return BACKUP_ID.test(backupId) && backupId.startsWith(`logical-full_${projectRef2}_`);
|
|
12584
12776
|
}
|
|
12585
12777
|
function verifiedBackup(value, projectRef2) {
|
|
12586
|
-
if (!
|
|
12778
|
+
if (!isRecord3(value) || typeof value.backup_id !== "string" || !backupBelongsToProject(value.backup_id, projectRef2) || value.project_ref !== projectRef2 || typeof value.database !== "string" || !SAFE_DATABASE.test(value.database) || value.kind !== "logical-full" || !canonicalTimestamp3(value.created_at) || !canonicalTimestamp3(value.completed_at) || new Date(value.completed_at).valueOf() < new Date(value.created_at).valueOf() || typeof value.bytes !== "number" || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || typeof value.sha256 !== "string" || !SHA256.test(value.sha256))
|
|
12587
12779
|
return null;
|
|
12588
12780
|
return {
|
|
12589
12781
|
backup_id: value.backup_id,
|
|
@@ -12597,7 +12789,7 @@ function verifiedBackup(value, projectRef2) {
|
|
|
12597
12789
|
};
|
|
12598
12790
|
}
|
|
12599
12791
|
function backupInventory(value, projectRef2) {
|
|
12600
|
-
if (!
|
|
12792
|
+
if (!isRecord3(value) || !Array.isArray(value.backups))
|
|
12601
12793
|
return null;
|
|
12602
12794
|
const backups = value.backups.map((backup) => verifiedBackup(backup, projectRef2));
|
|
12603
12795
|
if (backups.some((backup) => backup === null))
|
|
@@ -12676,7 +12868,7 @@ function readInventoryFailure(operation, read) {
|
|
|
12676
12868
|
return null;
|
|
12677
12869
|
}
|
|
12678
12870
|
function postgrestStatus(value) {
|
|
12679
|
-
if (!
|
|
12871
|
+
if (!isRecord3(value) || value.component !== "postgrest" || !["running", "stopped"].includes(String(value.desired)) || !["running", "stopped", "starting", "error"].includes(String(value.actual)) || !["healthy", "unhealthy", "unknown"].includes(String(value.health)))
|
|
12680
12872
|
return null;
|
|
12681
12873
|
return {
|
|
12682
12874
|
desired: value.desired,
|
|
@@ -12697,7 +12889,7 @@ function readPostgrestFailure(operation, read) {
|
|
|
12697
12889
|
return read.response.status === 200 && read.status ? null : releaseControlFailure(operation, "INVALID_RESPONSE", read.response.status);
|
|
12698
12890
|
}
|
|
12699
12891
|
function isRestartReceipt(value) {
|
|
12700
|
-
return
|
|
12892
|
+
return isRecord3(value) && value.service === "postgrest" && value.action === "restart" && value.success === true;
|
|
12701
12893
|
}
|
|
12702
12894
|
function registerReleaseTools(server, http, options = {}) {
|
|
12703
12895
|
server.tool("release", "Verified release controls using a Management API credential. Actions: logical_backup_list, logical_backup_create, logical_backup_restore, postgrest_status, postgrest_restart", {
|
|
@@ -12738,7 +12930,7 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
12738
12930
|
if (!mutation2.ok || mutation2.status !== 200) {
|
|
12739
12931
|
return mutationFailure("release.logical_backup.create", mutation2);
|
|
12740
12932
|
}
|
|
12741
|
-
const responseBackup =
|
|
12933
|
+
const responseBackup = isRecord3(mutation2.data) ? verifiedBackup(mutation2.data.backup, projectRef2) : null;
|
|
12742
12934
|
const afterFailure = readInventoryFailure("release.logical_backup.create", after);
|
|
12743
12935
|
const addedBackup = after.inventory && newlyCreatedBackup(before.inventory, after.inventory);
|
|
12744
12936
|
if (!responseBackup || afterFailure || !addedBackup || !equalBackup(responseBackup, addedBackup)) {
|
|
@@ -12763,7 +12955,7 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
12763
12955
|
if (!mutation2.ok || mutation2.status !== 200) {
|
|
12764
12956
|
return mutationFailure("release.logical_backup.restore", mutation2);
|
|
12765
12957
|
}
|
|
12766
|
-
const responseBackup =
|
|
12958
|
+
const responseBackup = isRecord3(mutation2.data) ? verifiedBackup(mutation2.data.restored_backup, projectRef2) : null;
|
|
12767
12959
|
const after = await readInventory(http, projectRef2);
|
|
12768
12960
|
const afterFailure = readInventoryFailure("release.logical_backup.restore", after);
|
|
12769
12961
|
const restoredInventoryBackup = after.inventory?.find((backup) => backup.backup_id === request.backup_id);
|
|
@@ -12803,7 +12995,7 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
12803
12995
|
// package.json
|
|
12804
12996
|
var package_default = {
|
|
12805
12997
|
name: "@supacloud/cli",
|
|
12806
|
-
version: "0.
|
|
12998
|
+
version: "0.28.0",
|
|
12807
12999
|
description: "Project-scoped CLI for SupaCloud users",
|
|
12808
13000
|
type: "module",
|
|
12809
13001
|
main: "./dist/index.js",
|
package/package.json
CHANGED
|
@@ -17,6 +17,7 @@ Load this reference when selecting a command surface or when a user asks an AI t
|
|
|
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
18
|
| Preview/apply migrations remotely | `supabase push` | Always dry-run first; production needs explicit approval |
|
|
19
19
|
| Mark proven-equivalent historical migrations as applied | `database baseline_migrations` | Dry-run, schema-equivalence proof, backup, explicit approval |
|
|
20
|
+
| Inspect auth users or generate a controlled login link | `auth list_users`, `auth get_user`, `auth generate_link` | User reads are bounded; login-link generation is a production-confirmed write and action links stay in the calling process |
|
|
20
21
|
| Manage Auth/Storage/Edge Functions/frontend/secrets | Corresponding project module | Keep deployable config/code in version control |
|
|
21
22
|
| Configure project gateway routes | `gateway` | Requires an admin-capable project token; inspect before write |
|
|
22
23
|
| Install/upgrade/debug SupaCloud servers | `supacloud-admin` | Platform boundary; not a project CLI action |
|
|
@@ -31,7 +32,7 @@ until a project-scoped context is resolved.
|
|
|
31
32
|
- `project`: selected-project metadata, authoritative endpoint projection, health, logs, API keys/settings, background tasks, retry/cancel, DLQ, and background settings. `project list` deliberately redirects to `supacloud-admin`; cross-project enumeration is not a project CLI capability.
|
|
32
33
|
- `database`: read/query, schema inspection, extensions, indexes, RLS, stats, migration push, controlled historical baseline, and SQL-file execution.
|
|
33
34
|
- `supabase`: allowlisted official CLI adapter for migration authoring, local reset/diff, explicit-DSN inspection/backup/type generation, and SupaCloud-controlled migration push.
|
|
34
|
-
- `auth`: provider and
|
|
35
|
+
- `auth`: provider/configuration plus bounded user lookup and production-confirmed login-link generation.
|
|
35
36
|
- `storage`: buckets and object-management workflows.
|
|
36
37
|
- `edge_functions`: list, atomically read one active or deleted identity with `get_config`, read immutable source, deploy, activate, configure, and delete Edge Functions. For every mutation, pass the `activation_id` read from the same `list` or `get_config` snapshot as `--expected-activation-id`; use `legacy` only for a never-created or listed legacy Function, not for a deleted slug with a tombstone UUID. Deploy and activate actions also require the non-negative observed version as `--expected-active-version`; use `absent` for a never-created slug or a `get_config` tombstone. Version `0` is a legacy version token and cannot be used as a source or activation target.
|
|
37
38
|
- `frontend`: list, build/deploy, domain, and deployment workflows.
|