@supacloud/cli 0.26.0 → 0.27.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
@@ -6458,8 +6458,9 @@ function resolveSupaCloudContext(env = process.env, cwd = process.cwd(), selecti
6458
6458
  // src/shared/execution-policy.ts
6459
6459
  var ACTION_POLICY = {
6460
6460
  project: {
6461
- read: ["get", "health", "logs", "api_keys", "settings", "tasks", "task_detail", "task_stats", "dlq", "background_settings"],
6462
- write: ["pause", "restore", "task_cancel", "task_retry", "update_background_settings"]
6461
+ read: ["get", "endpoints", "health", "logs", "api_keys", "settings", "tasks", "task_detail", "task_stats", "dlq", "background_settings"],
6462
+ write: ["pause", "restore", "task_cancel", "task_retry", "update_background_settings"],
6463
+ local: ["list"]
6463
6464
  },
6464
6465
  database: {
6465
6466
  read: ["list_tables", "describe_columns", "list_indexes", "list_constraints", "list_extensions", "rls_status", "rls_policies", "list_auth_users", "get_auth_user", "connections", "stats", "slow_queries", "list_migrations", "migration_inventory", "project_url", "generate_types"],
@@ -10191,6 +10192,115 @@ function projectGetRead(response, expectedRef) {
10191
10192
  return project ? successfulResult(project) : failedResult("Invalid project response");
10192
10193
  }
10193
10194
 
10195
+ // src/shared/tools/project-endpoint-read.ts
10196
+ var PROJECT_ENDPOINT_RESPONSE_MAX_BYTES = 256 * 1024;
10197
+ var PROJECT_ENDPOINT_LIST_RESPONSE_MAX_BYTES = 1024 * 1024;
10198
+ var PROJECT_REF_PATTERN4 = /^[a-z0-9-]{1,20}$/;
10199
+ var PROJECT_ENDPOINTS_SCHEMA = "supacloud.project-endpoints.v1";
10200
+ var PROJECT_ENDPOINT_SOURCES = new Set([
10201
+ "explicit_api_domain",
10202
+ "explicit_auth_domain",
10203
+ "explicit_studio_domain",
10204
+ "custom_domain",
10205
+ "derived_api_domain",
10206
+ "generated"
10207
+ ]);
10208
+ var ROOT_KEYS = new Set(["schema", "project_ref", "endpoints"]);
10209
+ var ENDPOINTS_KEYS = new Set(["api", "auth", "studio"]);
10210
+ var ENDPOINT_KEYS = new Set(["origin", "host", "scheme", "source", "aliases"]);
10211
+ var MAX_ALIASES = 64;
10212
+ function plainRecord2(candidate) {
10213
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
10214
+ return null;
10215
+ const prototype = Object.getPrototypeOf(candidate);
10216
+ return prototype === Object.prototype || prototype === null ? candidate : null;
10217
+ }
10218
+ function hasOnlyKeys2(record, allowedKeys) {
10219
+ return Object.keys(record).every((key) => allowedKeys.has(key));
10220
+ }
10221
+ function boundedText2(candidate, maxLength) {
10222
+ return typeof candidate === "string" && candidate.length > 0 && candidate.length <= maxLength && !/[\u0000-\u001f\u007f]/u.test(candidate) ? candidate : null;
10223
+ }
10224
+ function canonicalHost(candidate, scheme) {
10225
+ const host = boundedText2(candidate, 255);
10226
+ if (!host)
10227
+ return null;
10228
+ try {
10229
+ const parsed = new URL(`${scheme}://${host}`);
10230
+ return parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash || parsed.host !== host ? null : host;
10231
+ } catch {
10232
+ return null;
10233
+ }
10234
+ }
10235
+ function projectEndpoint2(candidate) {
10236
+ const endpoint = plainRecord2(candidate);
10237
+ if (!endpoint || !hasOnlyKeys2(endpoint, ENDPOINT_KEYS))
10238
+ return null;
10239
+ const scheme = endpoint.scheme === "http" || endpoint.scheme === "https" ? endpoint.scheme : null;
10240
+ const origin = boundedText2(endpoint.origin, 2048);
10241
+ const source = boundedText2(endpoint.source, 64);
10242
+ if (!scheme || !origin || !source || !PROJECT_ENDPOINT_SOURCES.has(source))
10243
+ return null;
10244
+ let parsedOrigin;
10245
+ try {
10246
+ parsedOrigin = new URL(origin);
10247
+ } catch {
10248
+ return null;
10249
+ }
10250
+ if (parsedOrigin.protocol !== `${scheme}:` || parsedOrigin.origin !== origin || parsedOrigin.username || parsedOrigin.password || parsedOrigin.pathname !== "/" || parsedOrigin.search || parsedOrigin.hash)
10251
+ return null;
10252
+ const host = canonicalHost(endpoint.host, scheme);
10253
+ if (!host || host !== parsedOrigin.host || !Array.isArray(endpoint.aliases) || endpoint.aliases.length > MAX_ALIASES)
10254
+ return null;
10255
+ const aliases = [];
10256
+ const seenAliases = new Set;
10257
+ for (const aliasCandidate of endpoint.aliases) {
10258
+ const alias = canonicalHost(aliasCandidate, scheme);
10259
+ if (!alias || alias === host || seenAliases.has(alias))
10260
+ return null;
10261
+ seenAliases.add(alias);
10262
+ aliases.push(alias);
10263
+ }
10264
+ return { origin, host, scheme, source, aliases };
10265
+ }
10266
+ function projectEndpointProjection(candidate) {
10267
+ const projection = plainRecord2(candidate);
10268
+ if (!projection || !hasOnlyKeys2(projection, ROOT_KEYS) || projection.schema !== PROJECT_ENDPOINTS_SCHEMA || typeof projection.project_ref !== "string" || !PROJECT_REF_PATTERN4.test(projection.project_ref))
10269
+ return null;
10270
+ const endpoints = plainRecord2(projection.endpoints);
10271
+ if (!endpoints || !hasOnlyKeys2(endpoints, ENDPOINTS_KEYS))
10272
+ return null;
10273
+ const api = projectEndpoint2(endpoints.api);
10274
+ const auth = projectEndpoint2(endpoints.auth);
10275
+ const studio = projectEndpoint2(endpoints.studio);
10276
+ return api && auth && studio ? {
10277
+ schema: PROJECT_ENDPOINTS_SCHEMA,
10278
+ project_ref: projection.project_ref,
10279
+ endpoints: { api, auth, studio }
10280
+ } : null;
10281
+ }
10282
+ function validHttpStatus2(status) {
10283
+ return Number.isSafeInteger(status) && status >= 100 && status <= 599;
10284
+ }
10285
+ function successfulResponse2(response) {
10286
+ return response.ok === true && validHttpStatus2(response.status) && response.status >= 200 && response.status <= 299;
10287
+ }
10288
+ function failedResult2(message) {
10289
+ return { text: `❌ ${message}`, isError: true };
10290
+ }
10291
+ function failedHttpResult2(label, status) {
10292
+ return failedResult2(validHttpStatus2(status) ? `${label} request failed (${status})` : `${label} request failed`);
10293
+ }
10294
+ function successfulResult2(payload) {
10295
+ return { text: JSON.stringify(payload, null, 2), isError: false };
10296
+ }
10297
+ function projectEndpointRead(response, expectedRef) {
10298
+ if (!successfulResponse2(response))
10299
+ return failedHttpResult2("Project endpoints", response.status);
10300
+ const projection = projectEndpointProjection(response.data);
10301
+ return projection && projection.project_ref === expectedRef ? successfulResult2(projection) : failedResult2("Invalid project endpoint response");
10302
+ }
10303
+
10194
10304
  // src/shared/tools/project-cli-tools.ts
10195
10305
  function projectReadResponse(readResult) {
10196
10306
  return {
@@ -10324,12 +10434,17 @@ function resolveRef(refFromArgs, defaultRef) {
10324
10434
  throw new Error("'ref' is required for this action");
10325
10435
  return ref;
10326
10436
  }
10437
+ function projectEndpointProjectionPath(ref) {
10438
+ return `/v1/projects/${encodeURIComponent(ref)}/endpoint/projection`;
10439
+ }
10327
10440
  function registerUserProjectCliTools(server, http, options = {}) {
10328
10441
  const { projectRef: projectRef2 } = options;
10329
10442
  server.tool("project", `Project-scoped inspection and developer operations.
10330
- Actions: get, pause, restore, health, logs, api_keys, settings, tasks, task_detail, task_cancel, task_retry, task_stats, dlq, background_settings, update_background_settings`, {
10443
+ Actions: list (Admin guidance), get, endpoints, pause, restore, health, logs, api_keys, settings, tasks, task_detail, task_cancel, task_retry, task_stats, dlq, background_settings, update_background_settings`, {
10331
10444
  action: withDescription(stringEnum([
10445
+ "list",
10332
10446
  "get",
10447
+ "endpoints",
10333
10448
  "pause",
10334
10449
  "restore",
10335
10450
  "health",
@@ -10352,6 +10467,19 @@ Actions: get, pause, restore, health, logs, api_keys, settings, tasks, task_deta
10352
10467
  concurrency: optional(Type.Number(), "[update_background_settings] Max concurrent background tasks"),
10353
10468
  max_attempts: optional(Type.Number(), "[update_background_settings] Max attempts for background tasks")
10354
10469
  }, async ({ action, ref, log_type, task_id, limit, concurrency, max_attempts }) => {
10470
+ if (action === "list") {
10471
+ return {
10472
+ isError: true,
10473
+ content: [{
10474
+ type: "text",
10475
+ text: [
10476
+ "⚠️ Project enumeration is a platform administration operation.",
10477
+ "Use `supacloud-admin project list` with an admin Management API context."
10478
+ ].join(`
10479
+ `)
10480
+ }]
10481
+ };
10482
+ }
10355
10483
  const resolvedRef = resolveRef(ref, projectRef2);
10356
10484
  let text;
10357
10485
  switch (action) {
@@ -10359,6 +10487,10 @@ Actions: get, pause, restore, health, logs, api_keys, settings, tasks, task_deta
10359
10487
  return projectReadResponse(projectGetRead(await http.get(`/v1/projects/${resolvedRef}`, {
10360
10488
  maxResponseBytes: PROJECT_READ_RESPONSE_MAX_BYTES
10361
10489
  }), resolvedRef));
10490
+ case "endpoints":
10491
+ return projectReadResponse(projectEndpointRead(await http.get(projectEndpointProjectionPath(resolvedRef), {
10492
+ maxResponseBytes: PROJECT_ENDPOINT_RESPONSE_MAX_BYTES
10493
+ }), resolvedRef));
10362
10494
  case "pause":
10363
10495
  text = simple(await http.post(`/v1/projects/${resolvedRef}/pause`), `Project ${resolvedRef} paused`);
10364
10496
  break;
@@ -12671,7 +12803,7 @@ function registerReleaseTools(server, http, options = {}) {
12671
12803
  // package.json
12672
12804
  var package_default = {
12673
12805
  name: "@supacloud/cli",
12674
- version: "0.26.0",
12806
+ version: "0.27.0",
12675
12807
  description: "Project-scoped CLI for SupaCloud users",
12676
12808
  type: "module",
12677
12809
  main: "./dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: supacloud-cli
3
- description: Use when operating, implementing, diagnosing, deploying, or documenting a SupaCloud project through supacloud-cli, especially database schema, functions/RPC, triggers, RLS, indexes, grants, extensions, migrations, backups, auth, storage, Edge Functions, frontend, queues, task events, diagnostics, or gateway work. Also use when an AI might otherwise call SQL, psql, a database API, or the Management API directly.
3
+ description: Use when operating, implementing, diagnosing, deploying, or documenting a SupaCloud project through supacloud-cli, especially project endpoint discovery, database schema, functions/RPC, triggers, RLS, indexes, grants, extensions, migrations, backups, auth, storage, Edge Functions, frontend, queues, task events, diagnostics, or gateway work. Also use when an AI might otherwise call SQL, psql, a database API, or the Management API directly.
4
4
  ---
5
5
 
6
6
  # SupaCloud CLI
@@ -24,6 +24,8 @@ Use `supacloud-cli` as the project-level control surface and keep durable change
24
24
  5. Run a remote migration dry-run before apply. Production apply requires explicit user approval in the current task.
25
25
  6. Do not edit `supabase_migrations.schema_migrations` through ordinary SQL. Migration history is an application ledger, not a schema backup or source of truth. For a proven-equivalent historical baseline, use the controlled `database baseline_migrations` action with dry-run and explicit approval.
26
26
  7. Service-role credentials authenticate the SupaCloud Management API. Never reinterpret them as PostgreSQL passwords or forward them to the official Supabase CLI.
27
+ 8. Read project domains through `supacloud-cli project endpoints`. Do not infer API, Auth, or Studio origins by concatenating project refs and base domains. The projection reports configuration, not DNS/certificate/runtime readiness.
28
+ 9. Cross-project enumeration is an Admin boundary. Use `supacloud-admin project list` or `supacloud-admin project list_endpoints`; do not attempt to widen project credentials.
27
29
 
28
30
  ## Workflow
29
31
 
@@ -36,6 +38,20 @@ Use `supacloud-cli` as the project-level control surface and keep durable change
36
38
  7. Apply only within the user-authorized environment and scope.
37
39
  8. Read back migration history and affected resources; report exact evidence and any remaining drift.
38
40
 
41
+ ## Project endpoint inspection
42
+
43
+ For the selected project:
44
+
45
+ ```bash
46
+ supacloud-cli status
47
+ supacloud-cli project endpoints
48
+ ```
49
+
50
+ The fixed projection contains credential-free API/Auth/Studio origins, hosts,
51
+ schemes, source classifications, and API aliases. Follow it with the relevant
52
+ health or gateway command before claiming that DNS, TLS, routing, or a runtime is
53
+ ready. Project-wide or fleet-wide inventories belong to `supacloud-admin`.
54
+
39
55
  ## Database default
40
56
 
41
57
  For a new database change:
@@ -66,8 +82,8 @@ supacloud-cli database baseline_migrations \
66
82
 
67
83
  ## CLI boundaries
68
84
 
69
- - `supacloud-cli`: project status, database, migrations, auth, storage, Edge Functions, frontend, queues, task events, diagnostics, and project gateway configuration.
70
- - `supacloud-admin`: installation, upgrades, SSH diagnostics, platform-wide project lifecycle, tenant runtime, and server operations.
85
+ - `supacloud-cli`: selected-project status and endpoint projection, database, migrations, auth, storage, Edge Functions, frontend, queues, task events, diagnostics, and project gateway configuration.
86
+ - `supacloud-admin`: installation, upgrades, SSH diagnostics, platform-wide project/endpoint inventory, project lifecycle, tenant runtime, and server operations.
71
87
  - Official `supabase` CLI: invoked only through the allowlisted `supacloud-cli supabase` adapter for supported local authoring or explicit-DSN inspection commands.
72
88
  - Direct HTTP/SQL: read-only diagnosis or an explicitly approved break-glass path; never the default implementation path.
73
89
 
@@ -7,6 +7,8 @@ Load this reference when selecting a command surface or when a user asks an AI t
7
7
  | Intent | Use | Guardrail |
8
8
  | --- | --- | --- |
9
9
  | Inspect current project binding | `supacloud-cli status` | Read-only; run first |
10
+ | Inspect selected project API/Auth/Studio origins | `supacloud-cli project endpoints` | Uses the Management API's authoritative projection; do not reconstruct domains locally |
11
+ | Enumerate projects or endpoint projections | `supacloud-admin project list` / `project list_endpoints` | Platform-wide read; never promote a project credential to Admin authority |
10
12
  | Inspect project health/logs/tasks | `project`, `queue`, `task_events`, `diagnostics` | Prefer bounded reads |
11
13
  | Read database rows or metadata | `database query` and database inspection actions | `SELECT`/read-only by default |
12
14
  | Create schema/function/RPC/trigger/RLS/index/grant/extension | `supabase migration_new`, then edit SQL | Never direct remote DDL |
@@ -26,7 +28,7 @@ until a project-scoped context is resolved.
26
28
  ## Command groups
27
29
 
28
30
  - `status`: resolved context, Management API connectivity, authentication, and project reachability.
29
- - `project`: project metadata, health, logs, API keys/settings, background tasks, retry/cancel, DLQ, and background settings.
31
+ - `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.
30
32
  - `database`: read/query, schema inspection, extensions, indexes, RLS, stats, migration push, controlled historical baseline, and SQL-file execution.
31
33
  - `supabase`: allowlisted official CLI adapter for migration authoring, local reset/diff, explicit-DSN inspection/backup/type generation, and SupaCloud-controlled migration push.
32
34
  - `auth`: provider and authentication configuration.
@@ -43,8 +45,14 @@ until a project-scoped context is resolved.
43
45
  ```bash
44
46
  supacloud-cli status
45
47
  supacloud-cli project get
48
+ supacloud-cli project endpoints
46
49
  supacloud-cli project health
47
50
  supacloud-cli supabase migration_list --db_url "$SUPACLOUD_DB_URL"
48
51
  ```
49
52
 
53
+ The endpoint projection returns bounded, credential-free API/Auth/Studio origins,
54
+ canonical hosts, URL schemes, configuration sources, and API aliases. It does
55
+ not assert DNS, certificate, or runtime readiness; use the relevant health and
56
+ gateway inspection commands for those checks.
57
+
50
58
  Do not paste the DSN value into chat or commit it to shell scripts. Prefer an environment variable supplied outside the repository.