@supacloud/admin 0.14.3 → 0.15.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.
Files changed (2) hide show
  1. package/dist/index.js +153 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -25791,6 +25791,8 @@ var ACTION_POLICY = {
25791
25791
  read: [
25792
25792
  "list",
25793
25793
  "get",
25794
+ "endpoints",
25795
+ "list_endpoints",
25794
25796
  "settings",
25795
25797
  "api_keys",
25796
25798
  "health",
@@ -25900,7 +25902,7 @@ function requestedProjectRef(moduleName, action, args) {
25900
25902
  if (moduleName === "ssh")
25901
25903
  return sshRequestedProjectRef(action, args);
25902
25904
  if (moduleName === "project") {
25903
- return ["list", "create"].includes(action) ? null : stringArgument(args, "ref");
25905
+ return ["list", "list_endpoints", "create"].includes(action) ? null : stringArgument(args, "ref");
25904
25906
  }
25905
25907
  if (moduleName === "platform") {
25906
25908
  return PLATFORM_PROJECT_REF_ACTIONS.has(action) ? stringArgument(args, "ref") : null;
@@ -30011,7 +30013,7 @@ async function cleanedCredentialState(cleanup, sanitized) {
30011
30013
  return sanitized ? "absent" : "unknown";
30012
30014
  }
30013
30015
  async function removeFailedEnvFile(cleanup) {
30014
- const { directoryHandle, filename, openFile, fileIdentity, operations } = cleanup;
30016
+ const { directoryHandle, openFile } = cleanup;
30015
30017
  if (!openFile) {
30016
30018
  try {
30017
30019
  await directoryHandle.close();
@@ -30427,6 +30429,133 @@ function projectGetRead(response, expectedRef) {
30427
30429
  const project = projectDetails(response.data, expectedRef);
30428
30430
  return project ? successfulResult(project) : failedResult("Invalid project response");
30429
30431
  }
30432
+ // ../cli/src/shared/tools/project-endpoint-read.ts
30433
+ var PROJECT_ENDPOINT_RESPONSE_MAX_BYTES = 256 * 1024;
30434
+ var PROJECT_ENDPOINT_LIST_RESPONSE_MAX_BYTES = 1024 * 1024;
30435
+ var PROJECT_REF_PATTERN2 = /^[a-z0-9-]{1,20}$/;
30436
+ var PROJECT_ENDPOINTS_SCHEMA = "supacloud.project-endpoints.v1";
30437
+ var PROJECT_ENDPOINT_SOURCES = new Set([
30438
+ "explicit_api_domain",
30439
+ "explicit_auth_domain",
30440
+ "explicit_studio_domain",
30441
+ "custom_domain",
30442
+ "derived_api_domain",
30443
+ "generated"
30444
+ ]);
30445
+ var ROOT_KEYS = new Set(["schema", "project_ref", "endpoints"]);
30446
+ var ENDPOINTS_KEYS = new Set(["api", "auth", "studio"]);
30447
+ var ENDPOINT_KEYS = new Set(["origin", "host", "scheme", "source", "aliases"]);
30448
+ var MAX_PROJECTS2 = 1e4;
30449
+ var MAX_ALIASES = 64;
30450
+ function plainRecord2(candidate) {
30451
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
30452
+ return null;
30453
+ const prototype = Object.getPrototypeOf(candidate);
30454
+ return prototype === Object.prototype || prototype === null ? candidate : null;
30455
+ }
30456
+ function hasOnlyKeys2(record, allowedKeys) {
30457
+ return Object.keys(record).every((key) => allowedKeys.has(key));
30458
+ }
30459
+ function boundedText2(candidate, maxLength) {
30460
+ return typeof candidate === "string" && candidate.length > 0 && candidate.length <= maxLength && !/[\u0000-\u001f\u007f]/u.test(candidate) ? candidate : null;
30461
+ }
30462
+ function canonicalHost(candidate, scheme) {
30463
+ const host = boundedText2(candidate, 255);
30464
+ if (!host)
30465
+ return null;
30466
+ try {
30467
+ const parsed = new URL(`${scheme}://${host}`);
30468
+ return parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash || parsed.host !== host ? null : host;
30469
+ } catch {
30470
+ return null;
30471
+ }
30472
+ }
30473
+ function projectEndpoint2(candidate) {
30474
+ const endpoint = plainRecord2(candidate);
30475
+ if (!endpoint || !hasOnlyKeys2(endpoint, ENDPOINT_KEYS))
30476
+ return null;
30477
+ const scheme = endpoint.scheme === "http" || endpoint.scheme === "https" ? endpoint.scheme : null;
30478
+ const origin = boundedText2(endpoint.origin, 2048);
30479
+ const source = boundedText2(endpoint.source, 64);
30480
+ if (!scheme || !origin || !source || !PROJECT_ENDPOINT_SOURCES.has(source))
30481
+ return null;
30482
+ let parsedOrigin;
30483
+ try {
30484
+ parsedOrigin = new URL(origin);
30485
+ } catch {
30486
+ return null;
30487
+ }
30488
+ if (parsedOrigin.protocol !== `${scheme}:` || parsedOrigin.origin !== origin || parsedOrigin.username || parsedOrigin.password || parsedOrigin.pathname !== "/" || parsedOrigin.search || parsedOrigin.hash)
30489
+ return null;
30490
+ const host = canonicalHost(endpoint.host, scheme);
30491
+ if (!host || host !== parsedOrigin.host || !Array.isArray(endpoint.aliases) || endpoint.aliases.length > MAX_ALIASES)
30492
+ return null;
30493
+ const aliases = [];
30494
+ const seenAliases = new Set;
30495
+ for (const aliasCandidate of endpoint.aliases) {
30496
+ const alias = canonicalHost(aliasCandidate, scheme);
30497
+ if (!alias || alias === host || seenAliases.has(alias))
30498
+ return null;
30499
+ seenAliases.add(alias);
30500
+ aliases.push(alias);
30501
+ }
30502
+ return { origin, host, scheme, source, aliases };
30503
+ }
30504
+ function projectEndpointProjection(candidate) {
30505
+ const projection = plainRecord2(candidate);
30506
+ if (!projection || !hasOnlyKeys2(projection, ROOT_KEYS) || projection.schema !== PROJECT_ENDPOINTS_SCHEMA || typeof projection.project_ref !== "string" || !PROJECT_REF_PATTERN2.test(projection.project_ref))
30507
+ return null;
30508
+ const endpoints = plainRecord2(projection.endpoints);
30509
+ if (!endpoints || !hasOnlyKeys2(endpoints, ENDPOINTS_KEYS))
30510
+ return null;
30511
+ const api = projectEndpoint2(endpoints.api);
30512
+ const auth = projectEndpoint2(endpoints.auth);
30513
+ const studio = projectEndpoint2(endpoints.studio);
30514
+ return api && auth && studio ? {
30515
+ schema: PROJECT_ENDPOINTS_SCHEMA,
30516
+ project_ref: projection.project_ref,
30517
+ endpoints: { api, auth, studio }
30518
+ } : null;
30519
+ }
30520
+ function validHttpStatus2(status) {
30521
+ return Number.isSafeInteger(status) && status >= 100 && status <= 599;
30522
+ }
30523
+ function successfulResponse2(response) {
30524
+ return response.ok === true && validHttpStatus2(response.status) && response.status >= 200 && response.status <= 299;
30525
+ }
30526
+ function failedResult2(message) {
30527
+ return { text: `❌ ${message}`, isError: true };
30528
+ }
30529
+ function failedHttpResult2(label, status) {
30530
+ return failedResult2(validHttpStatus2(status) ? `${label} request failed (${status})` : `${label} request failed`);
30531
+ }
30532
+ function successfulResult2(payload) {
30533
+ return { text: JSON.stringify(payload, null, 2), isError: false };
30534
+ }
30535
+ function projectEndpointRead(response, expectedRef) {
30536
+ if (!successfulResponse2(response))
30537
+ return failedHttpResult2("Project endpoints", response.status);
30538
+ const projection = projectEndpointProjection(response.data);
30539
+ return projection && projection.project_ref === expectedRef ? successfulResult2(projection) : failedResult2("Invalid project endpoint response");
30540
+ }
30541
+ function projectEndpointListRead(response) {
30542
+ if (!successfulResponse2(response))
30543
+ return failedHttpResult2("Project endpoint list", response.status);
30544
+ if (!Array.isArray(response.data) || response.data.length > MAX_PROJECTS2) {
30545
+ return failedResult2("Invalid project endpoint list response");
30546
+ }
30547
+ const projections = [];
30548
+ const refs = new Set;
30549
+ for (const candidate of response.data) {
30550
+ const projection = projectEndpointProjection(candidate);
30551
+ if (!projection || refs.has(projection.project_ref)) {
30552
+ return failedResult2("Invalid project endpoint list response");
30553
+ }
30554
+ refs.add(projection.project_ref);
30555
+ projections.push(projection);
30556
+ }
30557
+ return successfulResult2(projections);
30558
+ }
30430
30559
  // src/shared/tools/project-runtime-snapshot.ts
30431
30560
  var RUNTIME_SNAPSHOT_SCHEMA = "supacloud.runtime-snapshot.v1";
30432
30561
  var ATTESTED_REVISION_PATTERN = /^hmac-sha256:[a-f0-9]{64}$/;
@@ -30914,13 +31043,18 @@ function resolveRef(refFromArgs, defaultRef) {
30914
31043
  throw new Error("'ref' is required for this action");
30915
31044
  return ref;
30916
31045
  }
31046
+ function projectEndpointProjectionPath(ref) {
31047
+ return `/v1/projects/${encodeURIComponent(ref)}/endpoint/projection`;
31048
+ }
30917
31049
  function registerAdminProjectCliTools(server, http, options = {}) {
30918
31050
  const fileOperations = options.projectEnvFileOperations;
30919
- 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", {
31051
+ server.tool("project", "Platform-level project lifecycle management. Actions: list, list_endpoints, create, get, endpoints, delete, pause, restore, restart, settings, update_settings, api_keys, health, logs, tasks, services, runtime_snapshot, service_control", {
30920
31052
  action: withDescription(stringEnum([
30921
31053
  "list",
31054
+ "list_endpoints",
30922
31055
  "create",
30923
31056
  "get",
31057
+ "endpoints",
30924
31058
  "delete",
30925
31059
  "pause",
30926
31060
  "restore",
@@ -30935,7 +31069,7 @@ function registerAdminProjectCliTools(server, http, options = {}) {
30935
31069
  "runtime_snapshot",
30936
31070
  "service_control"
30937
31071
  ]), "Action to perform"),
30938
- ref: optional(Type.String(), "[*] Project ref (required for most actions except 'list' and 'create')"),
31072
+ ref: optional(Type.String(), "[*] Project ref (required for most actions except 'list', 'list_endpoints', and 'create')"),
30939
31073
  name: optional(Type.String(), "[create] Project name"),
30940
31074
  region: optional(Type.String(), "[create] Region (default: local)"),
30941
31075
  organization_id: optional(Type.String(), "[create] Organization ID"),
@@ -30972,6 +31106,10 @@ function registerAdminProjectCliTools(server, http, options = {}) {
30972
31106
  return projectReadResponse(projectListRead(await http.get("/v1/projects", {
30973
31107
  maxResponseBytes: PROJECT_READ_RESPONSE_MAX_BYTES
30974
31108
  })));
31109
+ case "list_endpoints":
31110
+ return projectReadResponse(projectEndpointListRead(await http.get("/v1/projects/endpoints", {
31111
+ maxResponseBytes: PROJECT_ENDPOINT_LIST_RESPONSE_MAX_BYTES
31112
+ })));
30975
31113
  case "create": {
30976
31114
  if (!name)
30977
31115
  throw new Error("'name' is required for create");
@@ -31015,6 +31153,12 @@ function registerAdminProjectCliTools(server, http, options = {}) {
31015
31153
  maxResponseBytes: PROJECT_READ_RESPONSE_MAX_BYTES
31016
31154
  }), resolvedRef));
31017
31155
  }
31156
+ case "endpoints": {
31157
+ const resolvedRef = resolveRef(ref);
31158
+ return projectReadResponse(projectEndpointRead(await http.get(projectEndpointProjectionPath(resolvedRef), {
31159
+ maxResponseBytes: PROJECT_ENDPOINT_RESPONSE_MAX_BYTES
31160
+ }), resolvedRef));
31161
+ }
31018
31162
  case "delete": {
31019
31163
  const resolvedRef = resolveRef(ref);
31020
31164
  text = simple(await http.delete(`/v1/projects/${resolvedRef}`), `Project ${resolvedRef} deleted`);
@@ -31425,7 +31569,7 @@ import { createHash as createHash3 } from "node:crypto";
31425
31569
  import { constants as fsConstants2 } from "node:fs";
31426
31570
  import { open as open2 } from "node:fs/promises";
31427
31571
  import { resolve as resolve3 } from "node:path";
31428
- var PROJECT_REF_PATTERN2 = /^[A-Za-z0-9_-]{1,20}$/u;
31572
+ var PROJECT_REF_PATTERN3 = /^[A-Za-z0-9_-]{1,20}$/u;
31429
31573
  var DEPLOYMENT_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/u;
31430
31574
  var RELEASE_ID_PATTERN = /^[0-9a-f]{64}$/u;
31431
31575
  var MUTATION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
@@ -31468,7 +31612,7 @@ function releaseRecord(candidate) {
31468
31612
  "created_at",
31469
31613
  "kind"
31470
31614
  ];
31471
- if (!exactKeys(record, keys) || record.schema !== "supacloud.frontend-release.v1" || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN2.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")
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")
31472
31616
  return null;
31473
31617
  return {
31474
31618
  project_ref: record.project_ref,
@@ -31514,7 +31658,7 @@ function releaseInventory(candidate, expected) {
31514
31658
  "releases",
31515
31659
  "next_cursor"
31516
31660
  ];
31517
- if (!exactKeys(record, keys) || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN2.test(record.project_ref) || typeof record.deployment_id !== "string" || !DEPLOYMENT_ID_PATTERN.test(record.deployment_id) || !Array.isArray(record.releases))
31661
+ if (!exactKeys(record, keys) || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN3.test(record.project_ref) || typeof record.deployment_id !== "string" || !DEPLOYMENT_ID_PATTERN.test(record.deployment_id) || !Array.isArray(record.releases))
31518
31662
  return null;
31519
31663
  const activeReleaseId = nullableIdentity(record.active_release_id, RELEASE_ID_PATTERN);
31520
31664
  const activeActivationId = nullableIdentity(record.active_activation_id, MUTATION_ID_PATTERN);
@@ -31535,7 +31679,7 @@ function releaseInventory(candidate, expected) {
31535
31679
  };
31536
31680
  }
31537
31681
  function releaseEndpoint(projectRef, deploymentId) {
31538
- if (!PROJECT_REF_PATTERN2.test(projectRef))
31682
+ if (!PROJECT_REF_PATTERN3.test(projectRef))
31539
31683
  throw new Error("'ref' is invalid for frontend releases");
31540
31684
  if (!DEPLOYMENT_ID_PATTERN.test(deploymentId))
31541
31685
  throw new Error("'id' is invalid for frontend releases");
@@ -31896,7 +32040,7 @@ Actions: list_releases, get_release, upload_release, activate_release`, {
31896
32040
  // package.json
31897
32041
  var package_default = {
31898
32042
  name: "@supacloud/admin",
31899
- version: "0.14.3",
32043
+ version: "0.15.1",
31900
32044
  description: "Platform administration CLI for SupaCloud operators",
31901
32045
  type: "module",
31902
32046
  main: "./dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/admin",
3
- "version": "0.14.3",
3
+ "version": "0.15.1",
4
4
  "description": "Platform administration CLI for SupaCloud operators",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",