@supacloud/admin 0.11.0 → 0.12.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 +2 -0
- package/dist/index.js +221 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,6 +80,7 @@ npx @supacloud/admin project create --name my-app --domain example.com \
|
|
|
80
80
|
--env_file /secure/path/.env.project-credentials.test --environment test
|
|
81
81
|
npx @supacloud/admin project list
|
|
82
82
|
npx @supacloud/admin project services --ref abc123
|
|
83
|
+
npx @supacloud/admin project runtime_snapshot --ref abc123
|
|
83
84
|
npx @supacloud/admin project service_control --ref abc123 --service gotrue --service_action stop
|
|
84
85
|
```
|
|
85
86
|
|
|
@@ -167,6 +168,7 @@ Project commands owned by this CLI:
|
|
|
167
168
|
- `project restart`
|
|
168
169
|
- `project update_settings`
|
|
169
170
|
- `project services` — read-only project service inventory
|
|
171
|
+
- `project runtime_snapshot` — strict read-only runtime revision and PostgREST attestation snapshot
|
|
170
172
|
- `project service_control` — constrained project service lifecycle control
|
|
171
173
|
|
|
172
174
|
`project create` never prints project credentials. Pass an absolute
|
package/dist/index.js
CHANGED
|
@@ -25788,7 +25788,17 @@ function resolveSupaCloudContext(env = process.env, cwd = process.cwd(), selecti
|
|
|
25788
25788
|
// src/shared/execution-policy.ts
|
|
25789
25789
|
var ACTION_POLICY = {
|
|
25790
25790
|
project: {
|
|
25791
|
-
read: [
|
|
25791
|
+
read: [
|
|
25792
|
+
"list",
|
|
25793
|
+
"get",
|
|
25794
|
+
"settings",
|
|
25795
|
+
"api_keys",
|
|
25796
|
+
"health",
|
|
25797
|
+
"logs",
|
|
25798
|
+
"tasks",
|
|
25799
|
+
"services",
|
|
25800
|
+
"runtime_snapshot"
|
|
25801
|
+
],
|
|
25792
25802
|
write: ["create", "delete", "pause", "restore", "restart", "update_settings"]
|
|
25793
25803
|
},
|
|
25794
25804
|
platform: {
|
|
@@ -26023,6 +26033,14 @@ function transportFailure(error) {
|
|
|
26023
26033
|
transportError: true
|
|
26024
26034
|
};
|
|
26025
26035
|
}
|
|
26036
|
+
function responseBodyFailure(status) {
|
|
26037
|
+
return {
|
|
26038
|
+
ok: false,
|
|
26039
|
+
status,
|
|
26040
|
+
data: { error: "Invalid Response", code: "INVALID_RESPONSE" },
|
|
26041
|
+
responseError: true
|
|
26042
|
+
};
|
|
26043
|
+
}
|
|
26026
26044
|
function validatedPostTimeout(options) {
|
|
26027
26045
|
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT;
|
|
26028
26046
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_POST_TIMEOUT_MS) {
|
|
@@ -26039,6 +26057,15 @@ function validatedGetResponseLimit(options) {
|
|
|
26039
26057
|
}
|
|
26040
26058
|
return maxBytes;
|
|
26041
26059
|
}
|
|
26060
|
+
function validatedStrictJsonLimit(options) {
|
|
26061
|
+
const maxBytes = options.maxJsonBytes;
|
|
26062
|
+
if (maxBytes === undefined)
|
|
26063
|
+
return;
|
|
26064
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
|
|
26065
|
+
throw new RangeError("HTTP JSON response byte limit must be a positive safe integer");
|
|
26066
|
+
}
|
|
26067
|
+
return maxBytes;
|
|
26068
|
+
}
|
|
26042
26069
|
function responseExceedsDeclaredLimit(response, maxBytes) {
|
|
26043
26070
|
const contentLength = response.headers.get("content-length");
|
|
26044
26071
|
return contentLength !== null && /^\d+$/u.test(contentLength) && Number(contentLength) > maxBytes;
|
|
@@ -26091,6 +26118,13 @@ async function boundedResponseJson(response, maxBytes) {
|
|
|
26091
26118
|
throw error;
|
|
26092
26119
|
}
|
|
26093
26120
|
}
|
|
26121
|
+
async function strictBoundedResponseJson(response, maxBytes) {
|
|
26122
|
+
const responseBytes = await boundedResponseBytes(response, maxBytes);
|
|
26123
|
+
if (responseBytes === null)
|
|
26124
|
+
throw new Error("HTTP JSON response exceeded its byte limit");
|
|
26125
|
+
const responseText = new TextDecoder("utf-8", { fatal: true }).decode(responseBytes);
|
|
26126
|
+
return JSON.parse(responseText);
|
|
26127
|
+
}
|
|
26094
26128
|
async function fetchWithTimeout(url, options, timeoutMs = DEFAULT_TIMEOUT) {
|
|
26095
26129
|
const controller = new AbortController;
|
|
26096
26130
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -26142,16 +26176,33 @@ class HttpTransport {
|
|
|
26142
26176
|
}
|
|
26143
26177
|
async get(path, options = {}) {
|
|
26144
26178
|
const maxResponseBytes = validatedGetResponseLimit(options);
|
|
26179
|
+
const maxJsonBytes = validatedStrictJsonLimit(options);
|
|
26180
|
+
if (maxResponseBytes !== undefined && maxJsonBytes !== undefined) {
|
|
26181
|
+
throw new RangeError("HTTP response limit options are mutually exclusive");
|
|
26182
|
+
}
|
|
26183
|
+
let response;
|
|
26145
26184
|
try {
|
|
26146
|
-
|
|
26185
|
+
response = await fetchWithRetry(`${this.baseUrl}${path}`, {
|
|
26147
26186
|
method: "GET",
|
|
26148
26187
|
headers: this.headers()
|
|
26149
26188
|
});
|
|
26150
|
-
const data = maxResponseBytes === undefined ? await res.json().catch(() => null) : await boundedResponseJson(res, maxResponseBytes);
|
|
26151
|
-
return { ok: res.ok, status: res.status, data };
|
|
26152
26189
|
} catch (error) {
|
|
26153
26190
|
return transportFailure(error);
|
|
26154
26191
|
}
|
|
26192
|
+
if (maxJsonBytes !== undefined) {
|
|
26193
|
+
try {
|
|
26194
|
+
const data2 = await strictBoundedResponseJson(response, maxJsonBytes);
|
|
26195
|
+
return { ok: response.ok, status: response.status, data: data2 };
|
|
26196
|
+
} catch {
|
|
26197
|
+
return responseBodyFailure(response.status);
|
|
26198
|
+
}
|
|
26199
|
+
}
|
|
26200
|
+
if (maxResponseBytes !== undefined) {
|
|
26201
|
+
const data2 = await boundedResponseJson(response, maxResponseBytes);
|
|
26202
|
+
return { ok: response.ok, status: response.status, data: data2 };
|
|
26203
|
+
}
|
|
26204
|
+
const data = await response.json().catch(() => null);
|
|
26205
|
+
return { ok: response.ok, status: response.status, data };
|
|
26155
26206
|
}
|
|
26156
26207
|
async post(path, body, options) {
|
|
26157
26208
|
const timeoutMs = validatedPostTimeout(options);
|
|
@@ -30003,6 +30054,152 @@ function projectGetRead(response, expectedRef) {
|
|
|
30003
30054
|
const project = projectDetails(response.data, expectedRef);
|
|
30004
30055
|
return project ? successfulResult(project) : failedResult("Invalid project response");
|
|
30005
30056
|
}
|
|
30057
|
+
// src/shared/tools/project-runtime-snapshot.ts
|
|
30058
|
+
var RUNTIME_SNAPSHOT_SCHEMA = "supacloud.runtime-snapshot.v1";
|
|
30059
|
+
var ATTESTED_REVISION_PATTERN = /^hmac-sha256:[a-f0-9]{64}$/;
|
|
30060
|
+
var SAFE_PROJECT_REF2 = /^[a-z0-9-]{1,20}$/;
|
|
30061
|
+
var SNAPSHOT_KEYS = ["schema", "project_ref", "captured_at", "secrets", "postgrest"];
|
|
30062
|
+
var SECRETS_KEYS = [
|
|
30063
|
+
"desired_revision",
|
|
30064
|
+
"loaded_revision",
|
|
30065
|
+
"load_state",
|
|
30066
|
+
"load_source",
|
|
30067
|
+
"matches_desired",
|
|
30068
|
+
"loaded_at"
|
|
30069
|
+
];
|
|
30070
|
+
var POSTGREST_KEYS = [
|
|
30071
|
+
"desired_revision",
|
|
30072
|
+
"loaded_revision",
|
|
30073
|
+
"attestation_state",
|
|
30074
|
+
"matches_desired",
|
|
30075
|
+
"desired",
|
|
30076
|
+
"actual",
|
|
30077
|
+
"health",
|
|
30078
|
+
"port",
|
|
30079
|
+
"unit",
|
|
30080
|
+
"loaded_at"
|
|
30081
|
+
];
|
|
30082
|
+
var SECRET_LOAD_STATES = ["current", "stale", "not_loaded", "unverified", "unreachable"];
|
|
30083
|
+
var SECRET_LOAD_SOURCES = ["management_api", "stale_cache", "file_fallback"];
|
|
30084
|
+
var POSTGREST_ATTESTATION_STATES = [
|
|
30085
|
+
"loaded",
|
|
30086
|
+
"stale",
|
|
30087
|
+
"drifted",
|
|
30088
|
+
"unverified_legacy",
|
|
30089
|
+
"stopped",
|
|
30090
|
+
"unreachable"
|
|
30091
|
+
];
|
|
30092
|
+
var POSTGREST_DESIRED_STATES = ["running", "stopped"];
|
|
30093
|
+
var POSTGREST_ACTUAL_STATES = ["running", "stopped", "starting", "error"];
|
|
30094
|
+
var POSTGREST_HEALTH_STATES = ["healthy", "unhealthy", "unknown"];
|
|
30095
|
+
function isRecord2(candidate) {
|
|
30096
|
+
return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
|
|
30097
|
+
}
|
|
30098
|
+
function hasExactKeys(candidate, expectedKeys) {
|
|
30099
|
+
const actualKeys = Object.keys(candidate).sort();
|
|
30100
|
+
const sortedExpectedKeys = [...expectedKeys].sort();
|
|
30101
|
+
return actualKeys.length === sortedExpectedKeys.length && actualKeys.every((key, index) => key === sortedExpectedKeys[index]);
|
|
30102
|
+
}
|
|
30103
|
+
function isEnumMember(candidate, members) {
|
|
30104
|
+
return typeof candidate === "string" && members.includes(candidate);
|
|
30105
|
+
}
|
|
30106
|
+
function isIsoTimestampOrNull(candidate) {
|
|
30107
|
+
if (candidate === null)
|
|
30108
|
+
return true;
|
|
30109
|
+
if (typeof candidate !== "string")
|
|
30110
|
+
return false;
|
|
30111
|
+
const timestamp = Date.parse(candidate);
|
|
30112
|
+
return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === candidate;
|
|
30113
|
+
}
|
|
30114
|
+
function isRevisionOrNull(candidate) {
|
|
30115
|
+
return candidate === null || typeof candidate === "string" && ATTESTED_REVISION_PATTERN.test(candidate);
|
|
30116
|
+
}
|
|
30117
|
+
function isBooleanOrNull(candidate) {
|
|
30118
|
+
return candidate === null || typeof candidate === "boolean";
|
|
30119
|
+
}
|
|
30120
|
+
function isUnloadedSecretsState(candidate) {
|
|
30121
|
+
return candidate.loaded_revision === null && candidate.load_source === null && candidate.matches_desired === null && candidate.loaded_at === null;
|
|
30122
|
+
}
|
|
30123
|
+
function isLoadedSecretsState(candidate) {
|
|
30124
|
+
if (candidate.load_state === "current") {
|
|
30125
|
+
return candidate.loaded_revision === candidate.desired_revision && candidate.load_source === "management_api" && candidate.matches_desired === true && candidate.loaded_at !== null;
|
|
30126
|
+
}
|
|
30127
|
+
return candidate.load_state === "stale" && candidate.loaded_revision !== null && candidate.loaded_revision !== candidate.desired_revision && candidate.load_source === "management_api" && candidate.matches_desired === false && candidate.loaded_at !== null;
|
|
30128
|
+
}
|
|
30129
|
+
function isUnverifiedSecretsState(candidate) {
|
|
30130
|
+
if (candidate.load_state !== "unverified" || candidate.matches_desired !== null || candidate.loaded_at === null)
|
|
30131
|
+
return false;
|
|
30132
|
+
if (candidate.loaded_revision === candidate.desired_revision) {
|
|
30133
|
+
return candidate.load_source === "management_api";
|
|
30134
|
+
}
|
|
30135
|
+
return candidate.loaded_revision === null && candidate.load_source !== null;
|
|
30136
|
+
}
|
|
30137
|
+
function hasValidSecretsState(candidate) {
|
|
30138
|
+
if (candidate.load_state === "not_loaded" || candidate.load_state === "unreachable") {
|
|
30139
|
+
return isUnloadedSecretsState(candidate);
|
|
30140
|
+
}
|
|
30141
|
+
return isLoadedSecretsState(candidate) || isUnverifiedSecretsState(candidate);
|
|
30142
|
+
}
|
|
30143
|
+
function isRuntimeSecretsSnapshot(payload) {
|
|
30144
|
+
if (!isRecord2(payload) || !hasExactKeys(payload, SECRETS_KEYS))
|
|
30145
|
+
return false;
|
|
30146
|
+
if (!isRevisionOrNull(payload.loaded_revision) || typeof payload.desired_revision !== "string" || !ATTESTED_REVISION_PATTERN.test(payload.desired_revision) || !isEnumMember(payload.load_state, SECRET_LOAD_STATES) || !(payload.load_source === null || isEnumMember(payload.load_source, SECRET_LOAD_SOURCES)) || !isBooleanOrNull(payload.matches_desired) || !isIsoTimestampOrNull(payload.loaded_at))
|
|
30147
|
+
return false;
|
|
30148
|
+
return hasValidSecretsState(payload);
|
|
30149
|
+
}
|
|
30150
|
+
function hasConsistentRevisionMatch(candidate) {
|
|
30151
|
+
if (candidate.loaded_revision === null)
|
|
30152
|
+
return candidate.matches_desired === null;
|
|
30153
|
+
return candidate.matches_desired === (candidate.loaded_revision === candidate.desired_revision);
|
|
30154
|
+
}
|
|
30155
|
+
function hasActivePostgrestProjection(candidate) {
|
|
30156
|
+
return candidate.actual === "running" && candidate.health === "healthy" || candidate.actual === "error" && candidate.health === "unhealthy";
|
|
30157
|
+
}
|
|
30158
|
+
function hasValidPostgrestState(candidate) {
|
|
30159
|
+
if (!hasConsistentRevisionMatch(candidate))
|
|
30160
|
+
return false;
|
|
30161
|
+
if (candidate.attestation_state === "loaded") {
|
|
30162
|
+
return candidate.matches_desired === true && candidate.actual === "running" && candidate.health === "healthy" && candidate.loaded_at !== null;
|
|
30163
|
+
}
|
|
30164
|
+
if (candidate.attestation_state === "stale") {
|
|
30165
|
+
return candidate.loaded_revision !== null && candidate.matches_desired === false && candidate.loaded_at !== null && hasActivePostgrestProjection(candidate);
|
|
30166
|
+
}
|
|
30167
|
+
if (candidate.attestation_state === "unverified_legacy")
|
|
30168
|
+
return candidate.loaded_revision === null;
|
|
30169
|
+
if (candidate.attestation_state === "stopped") {
|
|
30170
|
+
return candidate.loaded_revision === null && candidate.actual === "stopped" && candidate.health === "unknown" && candidate.loaded_at === null;
|
|
30171
|
+
}
|
|
30172
|
+
if (candidate.attestation_state === "unreachable")
|
|
30173
|
+
return candidate.loaded_revision === null;
|
|
30174
|
+
return candidate.attestation_state === "drifted";
|
|
30175
|
+
}
|
|
30176
|
+
function isPostgrestRuntimeSnapshot(payload, projectRef) {
|
|
30177
|
+
if (!isRecord2(payload) || !hasExactKeys(payload, POSTGREST_KEYS))
|
|
30178
|
+
return false;
|
|
30179
|
+
if (typeof payload.desired_revision !== "string" || !ATTESTED_REVISION_PATTERN.test(payload.desired_revision) || !isRevisionOrNull(payload.loaded_revision) || !isEnumMember(payload.attestation_state, POSTGREST_ATTESTATION_STATES) || !isBooleanOrNull(payload.matches_desired) || !isEnumMember(payload.desired, POSTGREST_DESIRED_STATES) || !isEnumMember(payload.actual, POSTGREST_ACTUAL_STATES) || !isEnumMember(payload.health, POSTGREST_HEALTH_STATES) || !Number.isSafeInteger(payload.port) || Number(payload.port) < 1 || Number(payload.port) > 65535 || payload.unit !== `supacloud-pgrst@${projectRef}` || !isIsoTimestampOrNull(payload.loaded_at))
|
|
30180
|
+
return false;
|
|
30181
|
+
return hasValidPostgrestState(payload);
|
|
30182
|
+
}
|
|
30183
|
+
function sanitizedSnapshot(snapshot) {
|
|
30184
|
+
return {
|
|
30185
|
+
schema: snapshot.schema,
|
|
30186
|
+
project_ref: snapshot.project_ref,
|
|
30187
|
+
captured_at: snapshot.captured_at,
|
|
30188
|
+
secrets: { ...snapshot.secrets },
|
|
30189
|
+
postgrest: { ...snapshot.postgrest }
|
|
30190
|
+
};
|
|
30191
|
+
}
|
|
30192
|
+
function hasCausalLoadTimestamps(snapshot) {
|
|
30193
|
+
const capturedAt = Date.parse(snapshot.captured_at);
|
|
30194
|
+
return [snapshot.secrets.loaded_at, snapshot.postgrest.loaded_at].every((loadedAt) => loadedAt === null || Date.parse(loadedAt) <= capturedAt);
|
|
30195
|
+
}
|
|
30196
|
+
function parseProjectRuntimeSnapshot(payload, requestedProjectRef2) {
|
|
30197
|
+
if (!SAFE_PROJECT_REF2.test(requestedProjectRef2) || !isRecord2(payload) || !hasExactKeys(payload, SNAPSHOT_KEYS) || payload.schema !== RUNTIME_SNAPSHOT_SCHEMA || payload.project_ref !== requestedProjectRef2 || !isIsoTimestampOrNull(payload.captured_at) || payload.captured_at === null || !isRuntimeSecretsSnapshot(payload.secrets) || !isPostgrestRuntimeSnapshot(payload.postgrest, requestedProjectRef2))
|
|
30198
|
+
return null;
|
|
30199
|
+
const snapshot = payload;
|
|
30200
|
+
return hasCausalLoadTimestamps(snapshot) ? sanitizedSnapshot(snapshot) : null;
|
|
30201
|
+
}
|
|
30202
|
+
|
|
30006
30203
|
// src/shared/tools/project-cli-tools.ts
|
|
30007
30204
|
var PROJECT_SERVICE_NAMES = [
|
|
30008
30205
|
"postgrest",
|
|
@@ -30034,9 +30231,10 @@ var STUDIO_PROJECT_SERVICE_STATUSES = [
|
|
|
30034
30231
|
];
|
|
30035
30232
|
var AUTH_RUNTIME_MANAGED_BY_OWNER = "AUTH_RUNTIME_MANAGED_BY_OWNER";
|
|
30036
30233
|
var AUTH_SERVICE_HOST_SUFFIX = "-auth";
|
|
30037
|
-
var
|
|
30234
|
+
var SAFE_PROJECT_REF3 = /^[a-z0-9-]{1,20}$/;
|
|
30038
30235
|
var SAFE_AUTHORITY_PROJECT_REF = /^[A-Za-z0-9_-]{1,20}$/;
|
|
30039
30236
|
var MAX_SERVICE_CONTROL_MESSAGE_LENGTH = 256;
|
|
30237
|
+
var MAX_RUNTIME_SNAPSHOT_BYTES = 64 * 1024;
|
|
30040
30238
|
var RELEASE_CONTROL_RESPONSE_SCHEMA = "supacloud.cli.release-control.v1";
|
|
30041
30239
|
var PROJECT_CREATE_OPERATION = "project.create";
|
|
30042
30240
|
var PROJECT_ENVIRONMENTS = ["test", "production"];
|
|
@@ -30250,7 +30448,7 @@ function projectServiceStatusOutput(status) {
|
|
|
30250
30448
|
function projectServicesResponse(projectRef, response) {
|
|
30251
30449
|
if (!response.ok)
|
|
30252
30450
|
return failedProjectServiceHttpResponse(response);
|
|
30253
|
-
if (!
|
|
30451
|
+
if (!SAFE_PROJECT_REF3.test(projectRef) || !Array.isArray(response.data) || response.data.length !== 5) {
|
|
30254
30452
|
return failedProjectServiceResponse("Project service inventory response is invalid");
|
|
30255
30453
|
}
|
|
30256
30454
|
if (!response.data.every((service) => isProjectServiceStatus(service, projectRef))) {
|
|
@@ -30263,6 +30461,15 @@ function projectServicesResponse(projectRef, response) {
|
|
|
30263
30461
|
const services = response.data.map(projectServiceStatusOutput);
|
|
30264
30462
|
return projectToolResponse(JSON.stringify({ project_ref: projectRef, services }, null, 2));
|
|
30265
30463
|
}
|
|
30464
|
+
function projectRuntimeSnapshotResponse(projectRef, response) {
|
|
30465
|
+
if (response.responseError) {
|
|
30466
|
+
return failedProjectServiceResponse("Project runtime snapshot response is invalid");
|
|
30467
|
+
}
|
|
30468
|
+
if (!response.ok)
|
|
30469
|
+
return failedProjectServiceHttpResponse(response);
|
|
30470
|
+
const snapshot = parseProjectRuntimeSnapshot(response.data, projectRef);
|
|
30471
|
+
return snapshot ? projectToolResponse(JSON.stringify(snapshot, null, 2)) : failedProjectServiceResponse("Project runtime snapshot response is invalid");
|
|
30472
|
+
}
|
|
30266
30473
|
function supportsProjectServiceAction(service, action) {
|
|
30267
30474
|
return SUPPORTED_PROJECT_SERVICE_ACTIONS[service].includes(action);
|
|
30268
30475
|
}
|
|
@@ -30336,7 +30543,7 @@ function resolveRef(refFromArgs, defaultRef) {
|
|
|
30336
30543
|
}
|
|
30337
30544
|
function registerAdminProjectCliTools(server, http, options = {}) {
|
|
30338
30545
|
const fileOperations = options.projectEnvFileOperations;
|
|
30339
|
-
server.tool("project", "Platform-level project lifecycle management. Actions: list, create, get, delete, pause, restore, restart, settings, update_settings, api_keys, health, logs, tasks, services, service_control", {
|
|
30546
|
+
server.tool("project", "Platform-level project lifecycle management. Actions: list, create, get, delete, pause, restore, restart, settings, update_settings, api_keys, health, logs, tasks, services, runtime_snapshot, service_control", {
|
|
30340
30547
|
action: withDescription(stringEnum([
|
|
30341
30548
|
"list",
|
|
30342
30549
|
"create",
|
|
@@ -30352,6 +30559,7 @@ function registerAdminProjectCliTools(server, http, options = {}) {
|
|
|
30352
30559
|
"logs",
|
|
30353
30560
|
"tasks",
|
|
30354
30561
|
"services",
|
|
30562
|
+
"runtime_snapshot",
|
|
30355
30563
|
"service_control"
|
|
30356
30564
|
]), "Action to perform"),
|
|
30357
30565
|
ref: optional(Type.String(), "[*] Project ref (required for most actions except 'list' and 'create')"),
|
|
@@ -30485,6 +30693,10 @@ function registerAdminProjectCliTools(server, http, options = {}) {
|
|
|
30485
30693
|
const resolvedRef = resolveRef(ref);
|
|
30486
30694
|
return projectServicesResponse(resolvedRef, await http.get(`/v1/projects/${encodeURIComponent(resolvedRef)}/services`));
|
|
30487
30695
|
}
|
|
30696
|
+
case "runtime_snapshot": {
|
|
30697
|
+
const resolvedRef = resolveRef(ref);
|
|
30698
|
+
return projectRuntimeSnapshotResponse(resolvedRef, await http.get(`/v1/projects/${encodeURIComponent(resolvedRef)}/runtime-snapshot`, { maxJsonBytes: MAX_RUNTIME_SNAPSHOT_BYTES }));
|
|
30699
|
+
}
|
|
30488
30700
|
case "service_control": {
|
|
30489
30701
|
const resolvedRef = resolveRef(ref);
|
|
30490
30702
|
if (!service)
|
|
@@ -30837,7 +31049,7 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
|
|
|
30837
31049
|
// package.json
|
|
30838
31050
|
var package_default = {
|
|
30839
31051
|
name: "@supacloud/admin",
|
|
30840
|
-
version: "0.
|
|
31052
|
+
version: "0.12.0",
|
|
30841
31053
|
description: "Platform administration CLI for SupaCloud operators",
|
|
30842
31054
|
type: "module",
|
|
30843
31055
|
main: "./dist/index.js",
|
|
@@ -30969,6 +31181,7 @@ EXAMPLES
|
|
|
30969
31181
|
supacloud-admin project create --name my-app --domain example.com --env_file /secure/path/.env.project-credentials.test --environment test
|
|
30970
31182
|
supacloud-admin project list
|
|
30971
31183
|
supacloud-admin project services --ref abc123
|
|
31184
|
+
supacloud-admin project runtime_snapshot --ref abc123
|
|
30972
31185
|
supacloud-admin project service_control --ref abc123 --service gotrue --service_action stop
|
|
30973
31186
|
supacloud-admin platform metrics
|
|
30974
31187
|
supacloud-admin gateway routes --ref abc123
|