@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.
|
|
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
|
-
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.
|