@supacloud/admin 0.11.0 → 0.12.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 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
 
@@ -88,7 +89,9 @@ npx @supacloud/admin project service_control --ref abc123 --service gotrue --ser
88
89
  Each component reports `status` as `ok`, `unknown`, or `error`; a failed probe
89
90
  never substitutes a guessed version. Binary evidence is bound to the active
90
91
  systemd `ExecStart`, and Web Console evidence comes from its component marker
91
- plus an explicit `tree_sha256` digest.
92
+ plus an explicit `tree_sha256` digest. An `unknown` component remains a valid
93
+ inventory result, while any `error` component makes the CLI exit non-zero after
94
+ printing the structured report.
92
95
 
93
96
  ## Verified platform upgrades
94
97
 
@@ -167,6 +170,7 @@ Project commands owned by this CLI:
167
170
  - `project restart`
168
171
  - `project update_settings`
169
172
  - `project services` — read-only project service inventory
173
+ - `project runtime_snapshot` — strict read-only runtime revision and PostgREST attestation snapshot
170
174
  - `project service_control` — constrained project service lifecycle control
171
175
 
172
176
  `project create` never prints project credentials. Pass an absolute
package/dist/index.js CHANGED
@@ -4759,7 +4759,7 @@ var require_utils = __commonJS((exports, module) => {
4759
4759
 
4760
4760
  // node_modules/ssh2/lib/protocol/crypto/build/Release/sshcrypto.node
4761
4761
  var require_sshcrypto = __commonJS((exports, module) => {
4762
- module.exports = __require("./sshcrypto-vd2k5hq9.node");
4762
+ module.exports = __require("./sshcrypto-8m50vnmb.node");
4763
4763
  });
4764
4764
 
4765
4765
  // node_modules/ssh2/lib/protocol/crypto/poly1305.js
@@ -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: ["list", "get", "settings", "api_keys", "health", "logs", "tasks", "services"],
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
- const res = await fetchWithRetry(`${this.baseUrl}${path}`, {
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);
@@ -28673,6 +28724,13 @@ async function platformVersions(ssh) {
28673
28724
  }
28674
28725
  };
28675
28726
  }
28727
+ function platformVersionsToolResult(report) {
28728
+ const isError = Object.values(report.components).some((component) => component.status === "error");
28729
+ return {
28730
+ content: [{ type: "text", text: JSON.stringify(report, null, 2) }],
28731
+ ...isError ? { isError: true } : {}
28732
+ };
28733
+ }
28676
28734
  function registerSshTools(server, ssh) {
28677
28735
  server.tool("ssh", `Server management via SSH. Available before & after SupaCloud installation.
28678
28736
  Actions: ping, setup, install, upgrade, versions, diagnose, exec, troubleshoot, container_logs, tenant_manage, tenant_list, tenant_inspect, tenant_diagnose, tenant_migrate`, {
@@ -28859,8 +28917,7 @@ ${upgradeExecution.stdout.slice(-300)}${edgeBoundary}`;
28859
28917
  break;
28860
28918
  }
28861
28919
  case "versions": {
28862
- text = JSON.stringify(await platformVersions(ssh), null, 2);
28863
- break;
28920
+ return platformVersionsToolResult(await platformVersions(ssh));
28864
28921
  }
28865
28922
  case "diagnose": {
28866
28923
  const cmds = [
@@ -30003,6 +30060,152 @@ function projectGetRead(response, expectedRef) {
30003
30060
  const project = projectDetails(response.data, expectedRef);
30004
30061
  return project ? successfulResult(project) : failedResult("Invalid project response");
30005
30062
  }
30063
+ // src/shared/tools/project-runtime-snapshot.ts
30064
+ var RUNTIME_SNAPSHOT_SCHEMA = "supacloud.runtime-snapshot.v1";
30065
+ var ATTESTED_REVISION_PATTERN = /^hmac-sha256:[a-f0-9]{64}$/;
30066
+ var SAFE_PROJECT_REF2 = /^[a-z0-9-]{1,20}$/;
30067
+ var SNAPSHOT_KEYS = ["schema", "project_ref", "captured_at", "secrets", "postgrest"];
30068
+ var SECRETS_KEYS = [
30069
+ "desired_revision",
30070
+ "loaded_revision",
30071
+ "load_state",
30072
+ "load_source",
30073
+ "matches_desired",
30074
+ "loaded_at"
30075
+ ];
30076
+ var POSTGREST_KEYS = [
30077
+ "desired_revision",
30078
+ "loaded_revision",
30079
+ "attestation_state",
30080
+ "matches_desired",
30081
+ "desired",
30082
+ "actual",
30083
+ "health",
30084
+ "port",
30085
+ "unit",
30086
+ "loaded_at"
30087
+ ];
30088
+ var SECRET_LOAD_STATES = ["current", "stale", "not_loaded", "unverified", "unreachable"];
30089
+ var SECRET_LOAD_SOURCES = ["management_api", "stale_cache", "file_fallback"];
30090
+ var POSTGREST_ATTESTATION_STATES = [
30091
+ "loaded",
30092
+ "stale",
30093
+ "drifted",
30094
+ "unverified_legacy",
30095
+ "stopped",
30096
+ "unreachable"
30097
+ ];
30098
+ var POSTGREST_DESIRED_STATES = ["running", "stopped"];
30099
+ var POSTGREST_ACTUAL_STATES = ["running", "stopped", "starting", "error"];
30100
+ var POSTGREST_HEALTH_STATES = ["healthy", "unhealthy", "unknown"];
30101
+ function isRecord2(candidate) {
30102
+ return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
30103
+ }
30104
+ function hasExactKeys(candidate, expectedKeys) {
30105
+ const actualKeys = Object.keys(candidate).sort();
30106
+ const sortedExpectedKeys = [...expectedKeys].sort();
30107
+ return actualKeys.length === sortedExpectedKeys.length && actualKeys.every((key, index) => key === sortedExpectedKeys[index]);
30108
+ }
30109
+ function isEnumMember(candidate, members) {
30110
+ return typeof candidate === "string" && members.includes(candidate);
30111
+ }
30112
+ function isIsoTimestampOrNull(candidate) {
30113
+ if (candidate === null)
30114
+ return true;
30115
+ if (typeof candidate !== "string")
30116
+ return false;
30117
+ const timestamp = Date.parse(candidate);
30118
+ return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === candidate;
30119
+ }
30120
+ function isRevisionOrNull(candidate) {
30121
+ return candidate === null || typeof candidate === "string" && ATTESTED_REVISION_PATTERN.test(candidate);
30122
+ }
30123
+ function isBooleanOrNull(candidate) {
30124
+ return candidate === null || typeof candidate === "boolean";
30125
+ }
30126
+ function isUnloadedSecretsState(candidate) {
30127
+ return candidate.loaded_revision === null && candidate.load_source === null && candidate.matches_desired === null && candidate.loaded_at === null;
30128
+ }
30129
+ function isLoadedSecretsState(candidate) {
30130
+ if (candidate.load_state === "current") {
30131
+ return candidate.loaded_revision === candidate.desired_revision && candidate.load_source === "management_api" && candidate.matches_desired === true && candidate.loaded_at !== null;
30132
+ }
30133
+ 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;
30134
+ }
30135
+ function isUnverifiedSecretsState(candidate) {
30136
+ if (candidate.load_state !== "unverified" || candidate.matches_desired !== null || candidate.loaded_at === null)
30137
+ return false;
30138
+ if (candidate.loaded_revision === candidate.desired_revision) {
30139
+ return candidate.load_source === "management_api";
30140
+ }
30141
+ return candidate.loaded_revision === null && candidate.load_source !== null;
30142
+ }
30143
+ function hasValidSecretsState(candidate) {
30144
+ if (candidate.load_state === "not_loaded" || candidate.load_state === "unreachable") {
30145
+ return isUnloadedSecretsState(candidate);
30146
+ }
30147
+ return isLoadedSecretsState(candidate) || isUnverifiedSecretsState(candidate);
30148
+ }
30149
+ function isRuntimeSecretsSnapshot(payload) {
30150
+ if (!isRecord2(payload) || !hasExactKeys(payload, SECRETS_KEYS))
30151
+ return false;
30152
+ 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))
30153
+ return false;
30154
+ return hasValidSecretsState(payload);
30155
+ }
30156
+ function hasConsistentRevisionMatch(candidate) {
30157
+ if (candidate.loaded_revision === null)
30158
+ return candidate.matches_desired === null;
30159
+ return candidate.matches_desired === (candidate.loaded_revision === candidate.desired_revision);
30160
+ }
30161
+ function hasActivePostgrestProjection(candidate) {
30162
+ return candidate.actual === "running" && candidate.health === "healthy" || candidate.actual === "error" && candidate.health === "unhealthy";
30163
+ }
30164
+ function hasValidPostgrestState(candidate) {
30165
+ if (!hasConsistentRevisionMatch(candidate))
30166
+ return false;
30167
+ if (candidate.attestation_state === "loaded") {
30168
+ return candidate.matches_desired === true && candidate.actual === "running" && candidate.health === "healthy" && candidate.loaded_at !== null;
30169
+ }
30170
+ if (candidate.attestation_state === "stale") {
30171
+ return candidate.loaded_revision !== null && candidate.matches_desired === false && candidate.loaded_at !== null && hasActivePostgrestProjection(candidate);
30172
+ }
30173
+ if (candidate.attestation_state === "unverified_legacy")
30174
+ return candidate.loaded_revision === null;
30175
+ if (candidate.attestation_state === "stopped") {
30176
+ return candidate.loaded_revision === null && candidate.actual === "stopped" && candidate.health === "unknown" && candidate.loaded_at === null;
30177
+ }
30178
+ if (candidate.attestation_state === "unreachable")
30179
+ return candidate.loaded_revision === null;
30180
+ return candidate.attestation_state === "drifted";
30181
+ }
30182
+ function isPostgrestRuntimeSnapshot(payload, projectRef) {
30183
+ if (!isRecord2(payload) || !hasExactKeys(payload, POSTGREST_KEYS))
30184
+ return false;
30185
+ 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))
30186
+ return false;
30187
+ return hasValidPostgrestState(payload);
30188
+ }
30189
+ function sanitizedSnapshot(snapshot) {
30190
+ return {
30191
+ schema: snapshot.schema,
30192
+ project_ref: snapshot.project_ref,
30193
+ captured_at: snapshot.captured_at,
30194
+ secrets: { ...snapshot.secrets },
30195
+ postgrest: { ...snapshot.postgrest }
30196
+ };
30197
+ }
30198
+ function hasCausalLoadTimestamps(snapshot) {
30199
+ const capturedAt = Date.parse(snapshot.captured_at);
30200
+ return [snapshot.secrets.loaded_at, snapshot.postgrest.loaded_at].every((loadedAt) => loadedAt === null || Date.parse(loadedAt) <= capturedAt);
30201
+ }
30202
+ function parseProjectRuntimeSnapshot(payload, requestedProjectRef2) {
30203
+ 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))
30204
+ return null;
30205
+ const snapshot = payload;
30206
+ return hasCausalLoadTimestamps(snapshot) ? sanitizedSnapshot(snapshot) : null;
30207
+ }
30208
+
30006
30209
  // src/shared/tools/project-cli-tools.ts
30007
30210
  var PROJECT_SERVICE_NAMES = [
30008
30211
  "postgrest",
@@ -30034,9 +30237,10 @@ var STUDIO_PROJECT_SERVICE_STATUSES = [
30034
30237
  ];
30035
30238
  var AUTH_RUNTIME_MANAGED_BY_OWNER = "AUTH_RUNTIME_MANAGED_BY_OWNER";
30036
30239
  var AUTH_SERVICE_HOST_SUFFIX = "-auth";
30037
- var SAFE_PROJECT_REF2 = /^[a-z0-9-]{1,20}$/;
30240
+ var SAFE_PROJECT_REF3 = /^[a-z0-9-]{1,20}$/;
30038
30241
  var SAFE_AUTHORITY_PROJECT_REF = /^[A-Za-z0-9_-]{1,20}$/;
30039
30242
  var MAX_SERVICE_CONTROL_MESSAGE_LENGTH = 256;
30243
+ var MAX_RUNTIME_SNAPSHOT_BYTES = 64 * 1024;
30040
30244
  var RELEASE_CONTROL_RESPONSE_SCHEMA = "supacloud.cli.release-control.v1";
30041
30245
  var PROJECT_CREATE_OPERATION = "project.create";
30042
30246
  var PROJECT_ENVIRONMENTS = ["test", "production"];
@@ -30250,7 +30454,7 @@ function projectServiceStatusOutput(status) {
30250
30454
  function projectServicesResponse(projectRef, response) {
30251
30455
  if (!response.ok)
30252
30456
  return failedProjectServiceHttpResponse(response);
30253
- if (!SAFE_PROJECT_REF2.test(projectRef) || !Array.isArray(response.data) || response.data.length !== 5) {
30457
+ if (!SAFE_PROJECT_REF3.test(projectRef) || !Array.isArray(response.data) || response.data.length !== 5) {
30254
30458
  return failedProjectServiceResponse("Project service inventory response is invalid");
30255
30459
  }
30256
30460
  if (!response.data.every((service) => isProjectServiceStatus(service, projectRef))) {
@@ -30263,6 +30467,15 @@ function projectServicesResponse(projectRef, response) {
30263
30467
  const services = response.data.map(projectServiceStatusOutput);
30264
30468
  return projectToolResponse(JSON.stringify({ project_ref: projectRef, services }, null, 2));
30265
30469
  }
30470
+ function projectRuntimeSnapshotResponse(projectRef, response) {
30471
+ if (response.responseError) {
30472
+ return failedProjectServiceResponse("Project runtime snapshot response is invalid");
30473
+ }
30474
+ if (!response.ok)
30475
+ return failedProjectServiceHttpResponse(response);
30476
+ const snapshot = parseProjectRuntimeSnapshot(response.data, projectRef);
30477
+ return snapshot ? projectToolResponse(JSON.stringify(snapshot, null, 2)) : failedProjectServiceResponse("Project runtime snapshot response is invalid");
30478
+ }
30266
30479
  function supportsProjectServiceAction(service, action) {
30267
30480
  return SUPPORTED_PROJECT_SERVICE_ACTIONS[service].includes(action);
30268
30481
  }
@@ -30336,7 +30549,7 @@ function resolveRef(refFromArgs, defaultRef) {
30336
30549
  }
30337
30550
  function registerAdminProjectCliTools(server, http, options = {}) {
30338
30551
  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", {
30552
+ 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
30553
  action: withDescription(stringEnum([
30341
30554
  "list",
30342
30555
  "create",
@@ -30352,6 +30565,7 @@ function registerAdminProjectCliTools(server, http, options = {}) {
30352
30565
  "logs",
30353
30566
  "tasks",
30354
30567
  "services",
30568
+ "runtime_snapshot",
30355
30569
  "service_control"
30356
30570
  ]), "Action to perform"),
30357
30571
  ref: optional(Type.String(), "[*] Project ref (required for most actions except 'list' and 'create')"),
@@ -30485,6 +30699,10 @@ function registerAdminProjectCliTools(server, http, options = {}) {
30485
30699
  const resolvedRef = resolveRef(ref);
30486
30700
  return projectServicesResponse(resolvedRef, await http.get(`/v1/projects/${encodeURIComponent(resolvedRef)}/services`));
30487
30701
  }
30702
+ case "runtime_snapshot": {
30703
+ const resolvedRef = resolveRef(ref);
30704
+ return projectRuntimeSnapshotResponse(resolvedRef, await http.get(`/v1/projects/${encodeURIComponent(resolvedRef)}/runtime-snapshot`, { maxJsonBytes: MAX_RUNTIME_SNAPSHOT_BYTES }));
30705
+ }
30488
30706
  case "service_control": {
30489
30707
  const resolvedRef = resolveRef(ref);
30490
30708
  if (!service)
@@ -30837,7 +31055,7 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
30837
31055
  // package.json
30838
31056
  var package_default = {
30839
31057
  name: "@supacloud/admin",
30840
- version: "0.11.0",
31058
+ version: "0.12.1",
30841
31059
  description: "Platform administration CLI for SupaCloud operators",
30842
31060
  type: "module",
30843
31061
  main: "./dist/index.js",
@@ -30969,6 +31187,7 @@ EXAMPLES
30969
31187
  supacloud-admin project create --name my-app --domain example.com --env_file /secure/path/.env.project-credentials.test --environment test
30970
31188
  supacloud-admin project list
30971
31189
  supacloud-admin project services --ref abc123
31190
+ supacloud-admin project runtime_snapshot --ref abc123
30972
31191
  supacloud-admin project service_control --ref abc123 --service gotrue --service_action stop
30973
31192
  supacloud-admin platform metrics
30974
31193
  supacloud-admin gateway routes --ref abc123
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/admin",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "Platform administration CLI for SupaCloud operators",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
Binary file