@supacloud/admin 0.15.1 → 0.15.3

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.
Files changed (3) hide show
  1. package/README.md +39 -2
  2. package/dist/index.js +138 -24
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -124,8 +124,8 @@ external Edge Runtime as a single rollback-capable transaction:
124
124
 
125
125
  ```bash
126
126
  npx @supacloud/admin ssh upgrade \
127
- --version 0.50.31 \
128
- --edge_runtime_version 0.16.8 \
127
+ --version 0.61.4 \
128
+ --edge_runtime_version 0.18.2 \
129
129
  --artifact_transport local \
130
130
  --github_proxy direct
131
131
  ```
@@ -150,6 +150,43 @@ enabled state. Component upgrades require persisted `EDGE_RUNTIME_MODE=external`
150
150
  embedded mode is rejected before release artifacts or services are changed.
151
151
  Local, remote, and direct server upgrades share one nonblocking host-wide lock.
152
152
 
153
+ Local artifact upgrades require Management 0.61.4 or newer. After stopping the
154
+ old Management service and before writing runtime secrets or running
155
+ `--init-db`, the target binary verifies that `DATABASE_URL` identifies the
156
+ Management control-plane database, rejects a registered tenant database, and
157
+ creates a verified custom-format PostgreSQL archive. Backups and their private
158
+ receipts are stored under
159
+ `/var/lib/supacloud/backups/control-plane-upgrades/<backup-id>/`; the five most
160
+ recent trusted backups are retained.
161
+
162
+ The inspection transaction remains open while `pg_dump` imports its exported
163
+ snapshot, so a different PostgreSQL server cannot satisfy the dump step. The
164
+ root process reserves the private archive first; the `postgres` identity receives
165
+ only that inherited output descriptor and never gains path access to the
166
+ root-only backup directory.
167
+
168
+ The inspection transaction also keeps its exported snapshot alive until staged
169
+ `--init-db` finishes. The child receives the database fingerprint and snapshot
170
+ only in its process environment, imports that live snapshot, verifies the
171
+ PostgreSQL cluster and database identity, and performs all initialization writes
172
+ inside that same transaction. This keeps the writes on one PostgreSQL backend
173
+ even when `DATABASE_URL` points through a transaction-pooling proxy. A copied
174
+ data directory, promoted standby, restored disk snapshot, or other node cannot
175
+ import the exporter-owned snapshot even when its static fingerprint matches.
176
+ The guard is not persisted or printed. A guard rejection restores the previous
177
+ runtime environment but leaves Management stopped for explicit reconciliation
178
+ instead of reconnecting it to an unverified target.
179
+
180
+ A committed transaction emits exactly one redacted
181
+ `supacloud.control-plane-upgrade-safety.v1` receipt. It contains only the backup
182
+ identifier and directory, byte count, SHA-256 digest, migration candidate
183
+ counts, checkpoint state, and completion time. Admin validates the exact
184
+ receipt schema before deleting the transient
185
+ status and log records. A missing or malformed receipt leaves those records in
186
+ place and requires reconciliation. Restoring a control-plane backup is a
187
+ separate destructive operation and requires an explicit, independently reviewed
188
+ recovery decision; the upgrade command never restores one automatically.
189
+
153
190
  Admin observes the remote transaction for up to 30 minutes. Reaching that
154
191
  deadline stops only local observation; it does not stop, clean up, or mark the
155
192
  remote transaction as failed. The CLI reports the unit, stage, status, log, and
package/dist/index.js CHANGED
@@ -26367,7 +26367,7 @@ function quoteShell(shellText) {
26367
26367
  // src/shared/releases/local-upgrade-bundle.ts
26368
26368
  import { spawn } from "node:child_process";
26369
26369
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
26370
- import { accessSync, chmodSync as chmodSync2, constants as fsConstants, createWriteStream, lstatSync, mkdirSync, mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, readdirSync, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "node:fs";
26370
+ import { accessSync, chmodSync as chmodSync2, constants as fsConstants, createWriteStream, lstatSync, mkdirSync, mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, readdirSync, renameSync, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "node:fs";
26371
26371
  import { get } from "node:https";
26372
26372
  import { tmpdir as tmpdir2 } from "node:os";
26373
26373
  import { basename, delimiter, join } from "node:path";
@@ -26381,6 +26381,14 @@ var RELEASE_CHECKSUMS_NAME = "SHA256SUMS";
26381
26381
  var RELEASE_REPOSITORY = "vibeunion/supacloud";
26382
26382
  var RELEASE_SOURCE_REF = "refs/heads/main";
26383
26383
  var RELEASE_SIGNER_WORKFLOW = `${RELEASE_REPOSITORY}/.github/workflows/release-please.yml`;
26384
+ var LEGACY_EDGE_RUNTIME_RELEASE_IDENTITY = {
26385
+ repository: "zuohuadong/supacloud",
26386
+ workflow: "zuohuadong/supacloud/.github/workflows/release-please.yml",
26387
+ component: "edge-runtime",
26388
+ version: "0.18.2",
26389
+ tag: "edge-runtime-v0.18.2",
26390
+ sourceCommit: "c6a3a87cf4e41e0f3dafadb018dd0c7d5b99b7d9"
26391
+ };
26384
26392
  var MEBIBYTE = 1024 * 1024;
26385
26393
  var RELEASE_BUNDLE_SIZE_LIMITS = {
26386
26394
  manifest: MEBIBYTE,
@@ -26487,6 +26495,16 @@ function parseArtifacts(candidate, component) {
26487
26495
  throw new Error(`Release manifest artifact ${oversized.name} exceeds its size limit`);
26488
26496
  return artifacts;
26489
26497
  }
26498
+ function parseIdentity(manifest, source, release) {
26499
+ const current = manifest.repository === RELEASE_REPOSITORY && manifest.workflow === RELEASE_SIGNER_WORKFLOW;
26500
+ const legacyEdgeRuntime = manifest.repository === LEGACY_EDGE_RUNTIME_RELEASE_IDENTITY.repository && manifest.workflow === LEGACY_EDGE_RUNTIME_RELEASE_IDENTITY.workflow && release.component === LEGACY_EDGE_RUNTIME_RELEASE_IDENTITY.component && release.version === LEGACY_EDGE_RUNTIME_RELEASE_IDENTITY.version && release.tag === LEGACY_EDGE_RUNTIME_RELEASE_IDENTITY.tag && source.commit === LEGACY_EDGE_RUNTIME_RELEASE_IDENTITY.sourceCommit;
26501
+ if (!current && !legacyEdgeRuntime)
26502
+ throw new Error("Release manifest identity is invalid");
26503
+ return {
26504
+ repository: manifest.repository,
26505
+ workflow: manifest.workflow
26506
+ };
26507
+ }
26490
26508
  function parseReleaseManifest(text, expected) {
26491
26509
  let candidate;
26492
26510
  try {
@@ -26496,15 +26514,16 @@ function parseReleaseManifest(text, expected) {
26496
26514
  }
26497
26515
  const manifest = manifestObject(candidate, "Release manifest");
26498
26516
  assertExactKeys(manifest, ["schemaVersion", "repository", "source", "workflow", "release", "artifacts"], "Release manifest");
26499
- if (manifest.schemaVersion !== 1 || manifest.repository !== RELEASE_REPOSITORY || manifest.workflow !== RELEASE_SIGNER_WORKFLOW) {
26517
+ if (manifest.schemaVersion !== 1)
26500
26518
  throw new Error("Release manifest identity is invalid");
26501
- }
26519
+ const source = parseSource(manifest.source);
26520
+ const release = parseRelease(manifest.release, expected);
26521
+ const identity = parseIdentity(manifest, source, release);
26502
26522
  return {
26503
26523
  schemaVersion: 1,
26504
- repository: RELEASE_REPOSITORY,
26505
- source: parseSource(manifest.source),
26506
- workflow: RELEASE_SIGNER_WORKFLOW,
26507
- release: parseRelease(manifest.release, expected),
26524
+ ...identity,
26525
+ source,
26526
+ release,
26508
26527
  artifacts: parseArtifacts(manifest.artifacts, expected.component)
26509
26528
  };
26510
26529
  }
@@ -26994,11 +27013,11 @@ function githubAttestationVerificationArguments(request) {
26994
27013
  "--bundle",
26995
27014
  request.bundlePath,
26996
27015
  "--repo",
26997
- RELEASE_REPOSITORY,
27016
+ request.manifest.repository,
26998
27017
  "--signer-workflow",
26999
- RELEASE_SIGNER_WORKFLOW,
27018
+ request.manifest.workflow,
27000
27019
  "--source-ref",
27001
- RELEASE_SOURCE_REF,
27020
+ request.manifest.source.ref,
27002
27021
  "--source-digest",
27003
27022
  request.manifest.source.commit,
27004
27023
  "--deny-self-hosted-runners",
@@ -27041,13 +27060,27 @@ async function downloadReleaseMetadata(component, version, directory) {
27041
27060
  rmSync2(metadataPath, { force: true });
27042
27061
  }
27043
27062
  }
27044
- async function downloadManifestAttestation(manifestPath, destination) {
27063
+ function manifestAttestationDownloadUrl(release, manifest, manifestDigest) {
27064
+ if (manifest.repository === RELEASE_REPOSITORY) {
27065
+ return `${ATTESTATIONS_API}/sha256:${manifestDigest}`;
27066
+ }
27067
+ if (manifest.repository === LEGACY_EDGE_RUNTIME_RELEASE_IDENTITY.repository) {
27068
+ return releaseAssetUrl(release, RELEASE_ATTESTATION_NAME);
27069
+ }
27070
+ throw new Error("Release manifest repository is not supported");
27071
+ }
27072
+ async function downloadManifestAttestation(request) {
27073
+ const { release, manifest, manifestPath, destination } = request;
27045
27074
  const responsePath = `${destination}.response`;
27046
27075
  try {
27047
- await downloadDirect(`${ATTESTATIONS_API}/sha256:${sha256File(manifestPath)}`, responsePath, RELEASE_BUNDLE_SIZE_LIMITS.attestation);
27048
- const bundles = serializeAttestationBundles(parseJsonFile(responsePath, "GitHub attestation response"));
27049
- writeFileSync2(destination, bundles, { mode: 384, flag: "wx" });
27050
- chmodSync2(destination, 384);
27076
+ await downloadDirect(manifestAttestationDownloadUrl(release, manifest, sha256File(manifestPath)), responsePath, RELEASE_BUNDLE_SIZE_LIMITS.attestation);
27077
+ if (manifest.repository === RELEASE_REPOSITORY) {
27078
+ const bundles = serializeAttestationBundles(parseJsonFile(responsePath, "GitHub attestation response"));
27079
+ writeFileSync2(destination, bundles, { mode: 384, flag: "wx" });
27080
+ chmodSync2(destination, 384);
27081
+ } else {
27082
+ renameSync(responsePath, destination);
27083
+ }
27051
27084
  } finally {
27052
27085
  rmSync2(responsePath, { force: true });
27053
27086
  }
@@ -27058,7 +27091,7 @@ async function downloadComponent(request) {
27058
27091
  const attestationPath = directChildPath(request.destination, RELEASE_ATTESTATION_NAME);
27059
27092
  await downloadDirect(releaseAssetUrl(release, RELEASE_MANIFEST_NAME), manifestPath, RELEASE_BUNDLE_SIZE_LIMITS.manifest);
27060
27093
  const manifest = parseReleaseManifest(readFileSync3(manifestPath, "utf8"), request);
27061
- await downloadManifestAttestation(manifestPath, attestationPath);
27094
+ await downloadManifestAttestation({ release, manifest, manifestPath, destination: attestationPath });
27062
27095
  await verifyManifestAttestation({
27063
27096
  artifactPath: manifestPath,
27064
27097
  bundlePath: attestationPath,
@@ -27192,6 +27225,9 @@ var REMOTE_RUN_ROOT = "/var/lib/supacloud/upgrade-runs";
27192
27225
  var REMOTE_LOG_ROOT = "/var/log/supacloud";
27193
27226
  var REMOTE_UPLOAD_ROOT = "/var/tmp";
27194
27227
  var REMOTE_COMMAND_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
27228
+ var CONTROL_PLANE_BACKUP_ROOT = "/var/lib/supacloud/backups/control-plane-upgrades";
27229
+ var CONTROL_PLANE_SAFETY_PREFIX = "SUPACLOUD_CONTROL_PLANE_UPGRADE_SAFETY=";
27230
+ var MINIMUM_CONTROL_PLANE_SAFETY_VERSION = [0, 61, 4];
27195
27231
  var POLL_INTERVAL_MS = 2000;
27196
27232
  var STATE_READ_ATTEMPTS = 3;
27197
27233
  var REMOTE_STATE_READ_TIMEOUT_MS = 15000;
@@ -27199,6 +27235,76 @@ var UPGRADE_OBSERVATION_TIMEOUT_MS = 30 * 60000;
27199
27235
 
27200
27236
  class RemoteUpgradeReconciliationError extends AggregateError {
27201
27237
  }
27238
+ function exactObjectKeys(candidate, expected) {
27239
+ const actual = Object.keys(candidate).sort();
27240
+ return actual.length === expected.length && actual.every((key, index) => key === [...expected].sort()[index]);
27241
+ }
27242
+ function nonNegativeIntegerRecord(candidate, expectedKeys) {
27243
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
27244
+ return false;
27245
+ const record = candidate;
27246
+ return exactObjectKeys(record, expectedKeys) && Object.values(record).every((value) => Number.isSafeInteger(value) && Number(value) >= 0);
27247
+ }
27248
+ function canonicalTimestamp(candidate) {
27249
+ if (typeof candidate !== "string")
27250
+ return false;
27251
+ const timestamp = new Date(candidate);
27252
+ return Number.isFinite(timestamp.valueOf()) && timestamp.toISOString() === candidate;
27253
+ }
27254
+ function parseControlPlaneSafetyEvidence(log) {
27255
+ const receiptLines = log.split(/\r?\n/).filter((line) => line.startsWith(CONTROL_PLANE_SAFETY_PREFIX));
27256
+ if (receiptLines.length !== 1)
27257
+ throw new Error("Remote upgrade did not emit one control-plane safety receipt");
27258
+ let candidate;
27259
+ try {
27260
+ candidate = JSON.parse(receiptLines[0].slice(CONTROL_PLANE_SAFETY_PREFIX.length));
27261
+ } catch {
27262
+ throw new Error("Remote control-plane safety receipt is not valid JSON");
27263
+ }
27264
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
27265
+ throw new Error("Remote control-plane safety receipt is invalid");
27266
+ }
27267
+ const receipt = candidate;
27268
+ const expectedKeys = [
27269
+ "backup_id",
27270
+ "backup_directory",
27271
+ "bytes",
27272
+ "candidate_counts",
27273
+ "completed_at",
27274
+ "current_key_checkpoint_present",
27275
+ "schema",
27276
+ "sha256"
27277
+ ];
27278
+ const candidateKeys = [
27279
+ "deprecated_webhook_secrets",
27280
+ "legacy_deployment_history_rows",
27281
+ "legacy_project_config_rows",
27282
+ "opaque_key_backfill_projects",
27283
+ "stored_secret_values"
27284
+ ];
27285
+ const validBackupId = typeof receipt.backup_id === "string" && /^control-plane-\d{8}T\d{6}Z-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(receipt.backup_id);
27286
+ const validDigest = typeof receipt.sha256 === "string" && /^[0-9a-f]{64}$/.test(receipt.sha256);
27287
+ if (!exactObjectKeys(receipt, expectedKeys) || receipt.schema !== "supacloud.control-plane-upgrade-safety.v1" || !validBackupId || receipt.backup_directory !== `${CONTROL_PLANE_BACKUP_ROOT}/${receipt.backup_id}` || !Number.isSafeInteger(receipt.bytes) || Number(receipt.bytes) <= 0 || !canonicalTimestamp(receipt.completed_at) || typeof receipt.current_key_checkpoint_present !== "boolean" || !validDigest || !nonNegativeIntegerRecord(receipt.candidate_counts, candidateKeys)) {
27288
+ throw new Error("Remote control-plane safety receipt is invalid");
27289
+ }
27290
+ return receipt;
27291
+ }
27292
+ function assertControlPlaneSafetyVersion(version) {
27293
+ const match = version.match(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/);
27294
+ if (!match)
27295
+ throw new Error("Management version must be an exact stable version");
27296
+ const requested = match.slice(1).map(Number);
27297
+ if (!requested.every(Number.isSafeInteger)) {
27298
+ throw new Error("Management version must be an exact stable version");
27299
+ }
27300
+ for (let index = 0;index < requested.length; index += 1) {
27301
+ if (requested[index] > MINIMUM_CONTROL_PLANE_SAFETY_VERSION[index])
27302
+ return;
27303
+ if (requested[index] < MINIMUM_CONTROL_PLANE_SAFETY_VERSION[index]) {
27304
+ throw new Error("Local artifact upgrades require Management 0.61.4 or newer with control-plane backup safety");
27305
+ }
27306
+ }
27307
+ }
27202
27308
  function quoteShell2(shellText) {
27203
27309
  return `'${shellText.split("'").join("'\\''")}'`;
27204
27310
  }
@@ -27254,7 +27360,7 @@ function buildRemotePreflightScript() {
27254
27360
  "export PATH",
27255
27361
  trustedInstalledGithubFunction(),
27256
27362
  "test -d /run/systemd/system || { echo 'systemd is not the active init system' >&2; exit 1; }",
27257
- 'for tool in systemctl systemd-run sha256sum stat realpath tar file find sort awk grep tail timeout flock install mktemp; do command -v "$tool" >/dev/null 2>&1 || { echo "Required local-upgrade tool is missing: $tool" >&2; exit 127; }; done',
27363
+ 'for tool in systemctl systemd-run sha256sum stat realpath tar file find sort awk grep tail timeout flock install mktemp setpriv id pg_dump pg_restore; do command -v "$tool" >/dev/null 2>&1 || { echo "Required local-upgrade tool is missing: $tool" >&2; exit 127; }; done',
27258
27364
  "tail -n 0 -- /dev/null >/dev/null 2>&1 || { echo 'A tail implementation with -n and -- support is required' >&2; exit 1; }",
27259
27365
  "systemd-run --help | grep -Eq -- '(^|[[:space:]])--collect([=[:space:]]|$)' || { echo 'systemd-run --collect is required' >&2; exit 1; }",
27260
27366
  "test -d /run/lock || { echo '/run/lock is unavailable' >&2; exit 1; }",
@@ -27839,12 +27945,19 @@ async function completedUpgradeOutput(ssh, paths) {
27839
27945
  } catch (error) {
27840
27946
  throw remoteReconciliationFailure("Upgrade succeeded but its retained log could not be read", [error], paths);
27841
27947
  }
27948
+ let safetyEvidence;
27949
+ try {
27950
+ safetyEvidence = parseControlPlaneSafetyEvidence(log);
27951
+ } catch (error) {
27952
+ throw remoteReconciliationFailure("Upgrade succeeded but control-plane safety evidence is invalid", [error], paths);
27953
+ }
27842
27954
  try {
27843
27955
  await cleanupRemoteRecords(ssh, paths);
27844
27956
  } catch (error) {
27845
27957
  throw remoteReconciliationFailure("Upgrade succeeded but remote evidence cleanup could not be confirmed", [error], paths);
27846
27958
  }
27847
27959
  return `✅ Upgrade done
27960
+ ${JSON.stringify(safetyEvidence)}
27848
27961
  ${log.slice(-1500)}`;
27849
27962
  }
27850
27963
  async function throwRemoteUpgradeFailure(ssh, paths, status) {
@@ -27908,6 +28021,7 @@ async function awaitRemoteUpgrade(ssh, paths) {
27908
28021
  }
27909
28022
  }
27910
28023
  async function executeLocalUpgradeTransfer(ssh, request) {
28024
+ assertControlPlaneSafetyVersion(request.managementVersion);
27911
28025
  const runId = randomUUID3();
27912
28026
  const paths = buildRemoteUpgradePaths(runId);
27913
28027
  const preflight = await remoteUpgradePreflight(ssh);
@@ -29367,7 +29481,7 @@ function isPlainRecord(candidate) {
29367
29481
  const prototype = Object.getPrototypeOf(candidate);
29368
29482
  return prototype === Object.prototype || prototype === null;
29369
29483
  }
29370
- function canonicalTimestamp(candidate) {
29484
+ function canonicalTimestamp2(candidate) {
29371
29485
  if (typeof candidate !== "string")
29372
29486
  return false;
29373
29487
  const timestamp = new Date(candidate);
@@ -29384,7 +29498,7 @@ function validBackupDatabase(candidate) {
29384
29498
  return typeof candidate.database === "string" && SAFE_DATABASE.test(candidate.database);
29385
29499
  }
29386
29500
  function validBackupTimestamps(candidate) {
29387
- return canonicalTimestamp(candidate.created_at) && canonicalTimestamp(candidate.completed_at) && new Date(candidate.completed_at).valueOf() >= new Date(candidate.created_at).valueOf();
29501
+ return canonicalTimestamp2(candidate.created_at) && canonicalTimestamp2(candidate.completed_at) && new Date(candidate.completed_at).valueOf() >= new Date(candidate.created_at).valueOf();
29388
29502
  }
29389
29503
  function validBackupEvidence(candidate) {
29390
29504
  return Number.isSafeInteger(candidate.bytes) && Number(candidate.bytes) > 0 && typeof candidate.sha256 === "string" && SHA256.test(candidate.sha256);
@@ -30273,7 +30387,7 @@ function matchingText(candidate, maxLength, pattern) {
30273
30387
  const candidateText = boundedText(candidate, maxLength);
30274
30388
  return candidateText && pattern.test(candidateText) ? candidateText : null;
30275
30389
  }
30276
- function canonicalTimestamp2(candidate) {
30390
+ function canonicalTimestamp3(candidate) {
30277
30391
  const timestamp = boundedText(candidate, 64);
30278
30392
  if (!timestamp)
30279
30393
  return null;
@@ -30288,7 +30402,7 @@ function projectedSummary(project) {
30288
30402
  organization_slug: matchingText(project.organization_slug, 128, SAFE_IDENTIFIER_PATTERN),
30289
30403
  name: boundedText(project.name, 100),
30290
30404
  region: matchingText(project.region, 64, REGION_PATTERN),
30291
- created_at: canonicalTimestamp2(project.created_at),
30405
+ created_at: canonicalTimestamp3(project.created_at),
30292
30406
  status: matchingText(project.status, 64, STATUS_PATTERN)
30293
30407
  };
30294
30408
  return Object.values(summary).every((field) => field !== null) ? summary : null;
@@ -31612,7 +31726,7 @@ function releaseRecord(candidate) {
31612
31726
  "created_at",
31613
31727
  "kind"
31614
31728
  ];
31615
- if (!exactKeys(record, keys) || record.schema !== "supacloud.frontend-release.v1" || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN3.test(record.project_ref) || typeof record.deployment_id !== "string" || !DEPLOYMENT_ID_PATTERN.test(record.deployment_id) || typeof record.release_id !== "string" || !RELEASE_ID_PATTERN.test(record.release_id) || record.sha256 !== record.release_id || typeof record.tree_sha256 !== "string" || !RELEASE_ID_PATTERN.test(record.tree_sha256) || !Number.isSafeInteger(record.size_bytes) || Number(record.size_bytes) < 1 || !Number.isSafeInteger(record.file_count) || Number(record.file_count) < 1 || !canonicalTimestamp3(record.created_at) || record.kind !== "prebuilt_static")
31729
+ if (!exactKeys(record, keys) || record.schema !== "supacloud.frontend-release.v1" || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN3.test(record.project_ref) || typeof record.deployment_id !== "string" || !DEPLOYMENT_ID_PATTERN.test(record.deployment_id) || typeof record.release_id !== "string" || !RELEASE_ID_PATTERN.test(record.release_id) || record.sha256 !== record.release_id || typeof record.tree_sha256 !== "string" || !RELEASE_ID_PATTERN.test(record.tree_sha256) || !Number.isSafeInteger(record.size_bytes) || Number(record.size_bytes) < 1 || !Number.isSafeInteger(record.file_count) || Number(record.file_count) < 1 || !canonicalTimestamp4(record.created_at) || record.kind !== "prebuilt_static")
31616
31730
  return null;
31617
31731
  return {
31618
31732
  project_ref: record.project_ref,
@@ -31635,7 +31749,7 @@ function releaseEnvelope(candidate, expected) {
31635
31749
  return null;
31636
31750
  return release;
31637
31751
  }
31638
- function canonicalTimestamp3(candidate) {
31752
+ function canonicalTimestamp4(candidate) {
31639
31753
  if (typeof candidate !== "string" || !TIMESTAMP_PATTERN.test(candidate))
31640
31754
  return false;
31641
31755
  const milliseconds = Date.parse(candidate);
@@ -32040,7 +32154,7 @@ Actions: list_releases, get_release, upload_release, activate_release`, {
32040
32154
  // package.json
32041
32155
  var package_default = {
32042
32156
  name: "@supacloud/admin",
32043
- version: "0.15.1",
32157
+ version: "0.15.3",
32044
32158
  description: "Platform administration CLI for SupaCloud operators",
32045
32159
  type: "module",
32046
32160
  main: "./dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/admin",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
4
4
  "description": "Platform administration CLI for SupaCloud operators",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",