@supacloud/cli 0.25.0 → 0.26.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.
Files changed (2) hide show
  1. package/dist/index.js +282 -69
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6473,6 +6473,10 @@ var ACTION_POLICY = {
6473
6473
  read: ["list_providers", "get_provider", "supported_providers", "get_settings", "get_config"],
6474
6474
  write: ["configure_provider", "update_provider", "disable_provider", "wechat_mini", "wechat_open", "update_settings", "update_config"]
6475
6475
  },
6476
+ oauth_clients: {
6477
+ read: ["list", "get"],
6478
+ write: ["create", "delete"]
6479
+ },
6476
6480
  storage: {
6477
6481
  read: ["status", "list_buckets", "get_bucket", "list_files"],
6478
6482
  write: ["create_bucket", "update_bucket", "delete_bucket", "upload_base64", "delete_file"]
@@ -8170,6 +8174,214 @@ ${JSON.stringify(r.data, null, 2)}` : `❌ Failed (${r.status}): ${JSON.stringif
8170
8174
  });
8171
8175
  }
8172
8176
 
8177
+ // src/shared/tools/oauth-client-tools.ts
8178
+ var RELEASE_CANARY_CLIENT_NAME = "supacloud-release-canary";
8179
+ var CLIENT_ID_PATTERN = /^[A-Za-z0-9._~-]{1,256}$/;
8180
+ var MAX_CLIENT_LIST_BYTES = 256 * 1024;
8181
+ var READ_TIMEOUT_MS = 5000;
8182
+ function isRecord(value) {
8183
+ return value !== null && typeof value === "object" && !Array.isArray(value);
8184
+ }
8185
+ function releaseCanaryCallbackUri(value) {
8186
+ if (typeof value !== "string" || !value.trim()) {
8187
+ throw new Error("'redirect_uri' is required");
8188
+ }
8189
+ let uri;
8190
+ try {
8191
+ uri = new URL(value);
8192
+ } catch {
8193
+ throw new Error("'redirect_uri' must be an absolute HTTPS or loopback HTTP URL");
8194
+ }
8195
+ const loopback = uri.hostname === "127.0.0.1" || uri.hostname === "[::1]";
8196
+ const isHttps = uri.protocol === "https:";
8197
+ const isPortBoundLoopback = uri.protocol === "http:" && loopback && Boolean(uri.port);
8198
+ if (!isHttps && !isPortBoundLoopback || !uri.hostname || uri.username || uri.password || uri.search || uri.hash) {
8199
+ throw new Error("'redirect_uri' must be an exact HTTPS callback or port-bound loopback HTTP callback without credentials, query, or fragment");
8200
+ }
8201
+ return uri.toString();
8202
+ }
8203
+ function createdClientId(value) {
8204
+ if (!isRecord(value))
8205
+ return null;
8206
+ try {
8207
+ return clientId(value.client_id);
8208
+ } catch {
8209
+ return null;
8210
+ }
8211
+ }
8212
+ function clientId(value) {
8213
+ if (typeof value !== "string" || !CLIENT_ID_PATTERN.test(value)) {
8214
+ throw new Error("'client_id' is invalid");
8215
+ }
8216
+ return value;
8217
+ }
8218
+ function projectRef(value) {
8219
+ if (typeof value !== "string" || !value.trim())
8220
+ throw new Error("'ref' is required");
8221
+ return projectRefPathSegment(value.trim(), "OAuth client");
8222
+ }
8223
+ function oauthClientsPath(ref) {
8224
+ return `/v1/projects/${encodeURIComponent(ref)}/auth/oauth-clients`;
8225
+ }
8226
+ function expectedClient(value, redirectUri) {
8227
+ if (!isRecord(value) || typeof value.client_id !== "string" || !CLIENT_ID_PATTERN.test(value.client_id) || value.client_name !== RELEASE_CANARY_CLIENT_NAME || value.client_type !== "public" || value.token_endpoint_auth_method !== "none" || !Array.isArray(value.redirect_uris) || value.redirect_uris.length !== 1 || !Array.isArray(value.grant_types) || value.grant_types.length !== 1 || value.grant_types[0] !== "authorization_code" || !Array.isArray(value.response_types) || value.response_types.length !== 1 || value.response_types[0] !== "code")
8228
+ return null;
8229
+ let callback;
8230
+ try {
8231
+ callback = releaseCanaryCallbackUri(value.redirect_uris[0]);
8232
+ } catch {
8233
+ return null;
8234
+ }
8235
+ if (redirectUri !== undefined && callback !== redirectUri)
8236
+ return null;
8237
+ return {
8238
+ client_id: value.client_id,
8239
+ client_name: RELEASE_CANARY_CLIENT_NAME,
8240
+ client_type: "public",
8241
+ token_endpoint_auth_method: "none",
8242
+ redirect_uris: [callback],
8243
+ grant_types: ["authorization_code"],
8244
+ response_types: ["code"]
8245
+ };
8246
+ }
8247
+ function clientInventory(value) {
8248
+ if (!isRecord(value) || !Array.isArray(value.clients))
8249
+ return null;
8250
+ const clients = value.clients.filter((client) => isRecord(client) && client.client_name === RELEASE_CANARY_CLIENT_NAME).map((client) => expectedClient(client));
8251
+ if (clients.some((client) => client === null))
8252
+ return null;
8253
+ const inventory = clients;
8254
+ return new Set(inventory.map((client) => client.client_id)).size === inventory.length ? inventory : null;
8255
+ }
8256
+ function readFailure(operation, response) {
8257
+ if (!response.ok) {
8258
+ return releaseControlFailure(operation, response.responseReadError ? "INVALID_RESPONSE" : "HTTP_ERROR", response.transportError ? null : response.status);
8259
+ }
8260
+ return null;
8261
+ }
8262
+ async function listClients(http, ref) {
8263
+ const response = await http.get(oauthClientsPath(ref), {
8264
+ maxJsonBytes: MAX_CLIENT_LIST_BYTES,
8265
+ responseTimeoutMs: READ_TIMEOUT_MS
8266
+ });
8267
+ return { response, clients: response.ok && response.status === 200 ? clientInventory(response.data) : null };
8268
+ }
8269
+ async function getClient(http, ref, id) {
8270
+ const response = await http.get(`${oauthClientsPath(ref)}/${encodeURIComponent(id)}`, {
8271
+ maxJsonBytes: MAX_CLIENT_LIST_BYTES,
8272
+ responseTimeoutMs: READ_TIMEOUT_MS
8273
+ });
8274
+ return { response, client: response.ok && response.status === 200 ? expectedClient(response.data) : null };
8275
+ }
8276
+ function exactSingleClient(inventory, redirectUri) {
8277
+ return inventory.length === 1 && inventory[0]?.redirect_uris[0] === redirectUri ? inventory[0] : null;
8278
+ }
8279
+ async function listReleaseCanaryClients(http, ref) {
8280
+ const read = await listClients(http, ref);
8281
+ const failure = readFailure("oauth_clients.list", read.response);
8282
+ if (failure)
8283
+ return failure;
8284
+ if (!read.clients)
8285
+ return releaseControlFailure("oauth_clients.list", "INVALID_RESPONSE", read.response.status);
8286
+ return releaseControlSuccess("oauth_clients.list", { project_ref: ref, clients: read.clients });
8287
+ }
8288
+ async function getReleaseCanaryClient(http, ref, id) {
8289
+ const read = await getClient(http, ref, id);
8290
+ const failure = readFailure("oauth_clients.get", read.response);
8291
+ if (failure)
8292
+ return failure;
8293
+ if (!read.client || read.client.client_id !== id) {
8294
+ return releaseControlFailure("oauth_clients.get", "INVALID_RESPONSE", read.response.status);
8295
+ }
8296
+ return releaseControlSuccess("oauth_clients.get", { project_ref: ref, client: read.client });
8297
+ }
8298
+ async function createReleaseCanaryClient(http, ref, redirectUri) {
8299
+ const before = await listClients(http, ref);
8300
+ const beforeFailure = readFailure("oauth_clients.create", before.response);
8301
+ if (beforeFailure)
8302
+ return beforeFailure;
8303
+ if (!before.clients)
8304
+ return releaseControlFailure("oauth_clients.create", "INVALID_RESPONSE", before.response.status);
8305
+ const existing = exactSingleClient(before.clients, redirectUri);
8306
+ if (existing) {
8307
+ return releaseControlSuccess("oauth_clients.create", {
8308
+ project_ref: ref,
8309
+ client: existing,
8310
+ reused: true
8311
+ });
8312
+ }
8313
+ if (before.clients.length > 0) {
8314
+ return releaseControlFailure("oauth_clients.create", "MUTATION_NOT_SUCCEEDED", null, { project_ref: ref });
8315
+ }
8316
+ const mutation = await http.postReleaseMutation(oauthClientsPath(ref), {
8317
+ client_type: "public",
8318
+ token_endpoint_auth_method: "none",
8319
+ redirect_uris: [redirectUri],
8320
+ grant_types: ["authorization_code"],
8321
+ client_name: RELEASE_CANARY_CLIENT_NAME
8322
+ });
8323
+ if (!mutation.ok) {
8324
+ return releaseControlMutationFailure("oauth_clients.create", mutation, { project_ref: ref });
8325
+ }
8326
+ const createdId = createdClientId(mutation.data);
8327
+ if (!createdId)
8328
+ return releaseControlFailure("oauth_clients.create", "OUTCOME_UNKNOWN", mutation.status, { project_ref: ref });
8329
+ const read = await getClient(http, ref, createdId);
8330
+ const readFailureResult = readFailure("oauth_clients.create", read.response);
8331
+ if (readFailureResult || !read.client || read.client.client_id !== createdId || read.client.redirect_uris[0] !== redirectUri) {
8332
+ return releaseControlFailure("oauth_clients.create", "OUTCOME_UNKNOWN", mutation.status, { project_ref: ref });
8333
+ }
8334
+ return releaseControlSuccess("oauth_clients.create", {
8335
+ project_ref: ref,
8336
+ client: read.client,
8337
+ reused: false
8338
+ });
8339
+ }
8340
+ async function deleteReleaseCanaryClient(http, ref, id, redirectUri) {
8341
+ const before = await getClient(http, ref, id);
8342
+ const beforeFailure = readFailure("oauth_clients.delete", before.response);
8343
+ if (beforeFailure)
8344
+ return beforeFailure;
8345
+ if (!before.client || before.client.client_id !== id || before.client.redirect_uris[0] !== redirectUri) {
8346
+ return releaseControlFailure("oauth_clients.delete", "MUTATION_NOT_SUCCEEDED", null, { project_ref: ref });
8347
+ }
8348
+ const mutation = await http.deleteReleaseMutation(`${oauthClientsPath(ref)}/${encodeURIComponent(id)}`);
8349
+ if (!mutation.ok || ![200, 204].includes(mutation.status)) {
8350
+ return releaseControlMutationFailure("oauth_clients.delete", mutation, { project_ref: ref });
8351
+ }
8352
+ const after = await listClients(http, ref);
8353
+ const afterFailure = readFailure("oauth_clients.delete", after.response);
8354
+ if (afterFailure || !after.clients || after.clients.some((client) => client.client_id === id)) {
8355
+ return releaseControlFailure("oauth_clients.delete", "OUTCOME_UNKNOWN", mutation.status, { project_ref: ref });
8356
+ }
8357
+ return releaseControlSuccess("oauth_clients.delete", {
8358
+ project_ref: ref,
8359
+ client_id: id,
8360
+ deleted: true
8361
+ });
8362
+ }
8363
+ function registerOAuthClientTools(server, http) {
8364
+ server.tool("oauth_clients", "Dedicated release-canary public OAuth client lifecycle. It only manages the exact supacloud-release-canary public authorization-code client and never returns client secrets.", {
8365
+ action: withDescription(stringEnum(["list", "get", "create", "delete"]), "OAuth client action"),
8366
+ ref: withDescription(Type.String(), "Central SupAuth project ref"),
8367
+ client_id: optional(Type.String(), "[get/delete] Exact release-canary public OAuth client ID"),
8368
+ redirect_uri: optional(Type.String(), "[create/delete] Exact HTTPS or port-bound RFC 8252 loopback callback")
8369
+ }, async ({ action, ref, client_id, redirect_uri }) => {
8370
+ const targetRef = projectRef(ref);
8371
+ if (action === "list")
8372
+ return listReleaseCanaryClients(http, targetRef);
8373
+ if (action === "get")
8374
+ return getReleaseCanaryClient(http, targetRef, clientId(client_id));
8375
+ if (action === "create") {
8376
+ return createReleaseCanaryClient(http, targetRef, releaseCanaryCallbackUri(redirect_uri));
8377
+ }
8378
+ if (action === "delete") {
8379
+ return deleteReleaseCanaryClient(http, targetRef, clientId(client_id), releaseCanaryCallbackUri(redirect_uri));
8380
+ }
8381
+ throw new Error("Unknown OAuth client action");
8382
+ });
8383
+ }
8384
+
8173
8385
  // src/shared/tools/storage-tools.ts
8174
8386
  var MAX_BUCKET_ID_LENGTH = 100;
8175
8387
  var MAX_MIME_TYPE_COUNT = 100;
@@ -9184,13 +9396,13 @@ async function readFunctionSource(http, request) {
9184
9396
  function mutationIdentityMatches2(receipt, expectation) {
9185
9397
  return receipt.success === true && receipt.project_ref === expectation.projectRef && receipt.slug === expectation.slug && receipt.previous_active_version === expectation.expectedActiveVersion && receipt.expected_activation_id === expectation.expectedActivationId;
9186
9398
  }
9187
- async function readFunctionIdentity(http, projectRef, slug) {
9188
- const resourcePath = edgeFunctionResourcePath(projectRef, slug);
9399
+ async function readFunctionIdentity(http, projectRef2, slug) {
9400
+ const resourcePath = edgeFunctionResourcePath(projectRef2, slug);
9189
9401
  const response = await http.get(`${resourcePath}/config`);
9190
9402
  if (!response.ok) {
9191
9403
  return releaseControlFailure("edge_functions.get_config", "HTTP_ERROR", response.status);
9192
9404
  }
9193
- const identity = projectedFunctionIdentity(response.data, projectRef, slug);
9405
+ const identity = projectedFunctionIdentity(response.data, projectRef2, slug);
9194
9406
  return identity ? { content: [{ type: "text", text: JSON.stringify(identity, null, 2) }] } : releaseControlFailure("edge_functions.get_config", "INVALID_RESPONSE", response.status);
9195
9407
  }
9196
9408
  async function updateFunctionConfiguration(http, request) {
@@ -9253,15 +9465,15 @@ function readOnlyActivationResult() {
9253
9465
  };
9254
9466
  }
9255
9467
  function functionActivationTarget(args) {
9256
- const projectRef = typeof args.ref === "string" ? args.ref.trim() : "";
9468
+ const projectRef2 = typeof args.ref === "string" ? args.ref.trim() : "";
9257
9469
  const functionSlug = typeof args.slug === "string" ? args.slug.trim() : "";
9258
9470
  const version = positiveFunctionVersion(args.version, "Function activation version");
9259
- projectRefPathSegment(projectRef, "Edge Function activation");
9471
+ projectRefPathSegment(projectRef2, "Edge Function activation");
9260
9472
  if (!SAFE_FUNCTION_SLUG_PATTERN.test(functionSlug))
9261
9473
  throw new Error("'slug' is invalid for 'activate'");
9262
9474
  const expectedActiveVersion = requiredExpectedActiveVersion(args, "activate");
9263
9475
  const expectedActivationId = requiredExpectedActivationId(args, "activate");
9264
- return { projectRef, functionSlug, version, expectedActiveVersion, expectedActivationId };
9476
+ return { projectRef: projectRef2, functionSlug, version, expectedActiveVersion, expectedActivationId };
9265
9477
  }
9266
9478
  function requiredExpectedActiveVersion(args, action) {
9267
9479
  const expected = args["expected-active-version"];
@@ -9289,11 +9501,11 @@ async function activateFunctionVersion(http, args, readOnly = false) {
9289
9501
  const unsupported = Object.keys(args).filter((name) => !FUNCTION_ACTIVATION_ARGUMENTS.has(name));
9290
9502
  if (unsupported.length > 0)
9291
9503
  throw new Error(`'${unsupported[0]}' is not supported for 'activate'`);
9292
- const { projectRef, functionSlug, version, expectedActiveVersion, expectedActivationId } = functionActivationTarget(args);
9293
- const endpoint = edgeFunctionResourcePath(projectRef, functionSlug) + `/versions/${encodeURIComponent(version)}/activate`;
9504
+ const { projectRef: projectRef2, functionSlug, version, expectedActiveVersion, expectedActivationId } = functionActivationTarget(args);
9505
+ const endpoint = edgeFunctionResourcePath(projectRef2, functionSlug) + `/versions/${encodeURIComponent(version)}/activate`;
9294
9506
  return functionMutationResponse({
9295
9507
  operation: "edge_functions.activate",
9296
- projectRef,
9508
+ projectRef: projectRef2,
9297
9509
  slug: functionSlug,
9298
9510
  expectedActiveVersion,
9299
9511
  expectedActivationId,
@@ -10113,7 +10325,7 @@ function resolveRef(refFromArgs, defaultRef) {
10113
10325
  return ref;
10114
10326
  }
10115
10327
  function registerUserProjectCliTools(server, http, options = {}) {
10116
- const { projectRef } = options;
10328
+ const { projectRef: projectRef2 } = options;
10117
10329
  server.tool("project", `Project-scoped inspection and developer operations.
10118
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`, {
10119
10331
  action: withDescription(stringEnum([
@@ -10133,14 +10345,14 @@ Actions: get, pause, restore, health, logs, api_keys, settings, tasks, task_deta
10133
10345
  "background_settings",
10134
10346
  "update_background_settings"
10135
10347
  ]), "Action to perform"),
10136
- ref: optional(Type.String(), projectRef ? "Optional override when not auto-linked" : "Project ref"),
10348
+ ref: optional(Type.String(), projectRef2 ? "Optional override when not auto-linked" : "Project ref"),
10137
10349
  log_type: optional(stringEnum(["all", "auth", "database", "api"]), "[logs] Filter by service"),
10138
10350
  task_id: optional(Type.String(), "[task_detail/task_cancel/task_retry] Task ID"),
10139
10351
  limit: optional(Type.Number(), "[tasks/dlq] Max items to return"),
10140
10352
  concurrency: optional(Type.Number(), "[update_background_settings] Max concurrent background tasks"),
10141
10353
  max_attempts: optional(Type.Number(), "[update_background_settings] Max attempts for background tasks")
10142
10354
  }, async ({ action, ref, log_type, task_id, limit, concurrency, max_attempts }) => {
10143
- const resolvedRef = resolveRef(ref, projectRef);
10355
+ const resolvedRef = resolveRef(ref, projectRef2);
10144
10356
  let text;
10145
10357
  switch (action) {
10146
10358
  case "get":
@@ -10311,7 +10523,7 @@ function resolveRef2(refFromArgs, defaultRef) {
10311
10523
  return ref;
10312
10524
  }
10313
10525
  function registerQueueTools(server, http, options = {}) {
10314
- const { projectRef } = options;
10526
+ const { projectRef: projectRef2 } = options;
10315
10527
  server.tool("queue", `Message queue operations for task-based messaging.
10316
10528
  Actions: list, stats, list_messages, dlq, get_message, send, receive, ack, release, fail, retry, delete_message, get_settings, update_settings`, {
10317
10529
  action: withDescription(stringEnum([
@@ -10351,7 +10563,7 @@ Actions: list, stats, list_messages, dlq, get_message, send, receive, ack, relea
10351
10563
  max_attempts_setting: optional(Type.Number(), "[update_settings] Max delivery attempts"),
10352
10564
  rate_limit: optional(Type.Number(), "[update_settings] Rate limit per minute")
10353
10565
  }, async (args) => {
10354
- const resolvedRef = resolveRef2(args.ref, projectRef);
10566
+ const resolvedRef = resolveRef2(args.ref, projectRef2);
10355
10567
  const q = args.queue;
10356
10568
  const need = (fields) => {
10357
10569
  for (const f of fields) {
@@ -10551,7 +10763,7 @@ var redirectStatus = Type.Optional(Type.Union([
10551
10763
  var ok2 = (res) => res.ok ? JSON.stringify(res.data, null, 2) : `❌ Failed (${res.status}): ${JSON.stringify(res.data)}`;
10552
10764
  var simple2 = (res, msg) => res.ok ? `✅ ${msg}` : `❌ Failed (${res.status}): ${JSON.stringify(res.data)}`;
10553
10765
  function registerGatewayTools(server, http, options = {}) {
10554
- const { projectRef } = options;
10766
+ const { projectRef: projectRef2 } = options;
10555
10767
  server.tool("gateway", `Gateway / Caddy 配置(通过 JSON Admin API 注入)。要求 admin 权限。
10556
10768
  Actions: routes, upsert_route, update_route, delete_route, config, get_certificate, update_certificate, issue_certificate, deploy_certificate, rebuild, custom_hostname, set_custom_hostname, delete_custom_hostname, verify_custom_hostname`, {
10557
10769
  action: withDescription(stringEnum([
@@ -10570,7 +10782,7 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
10570
10782
  "delete_custom_hostname",
10571
10783
  "verify_custom_hostname"
10572
10784
  ]), "Action"),
10573
- ref: optional(Type.String(), projectRef ? "可选:覆盖自动关联的项目 ref" : "项目 ref"),
10785
+ ref: optional(Type.String(), projectRef2 ? "可选:覆盖自动关联的项目 ref" : "项目 ref"),
10574
10786
  route_id: optional(Type.String(), "[upsert_route/update_route/delete_route] 路由 ID(字母/数字/_/-,1-64)"),
10575
10787
  hosts: withDescription(stringArray, "[upsert_route/update_route] 主机名列表,逗号分隔或 JSON 数组(1-20)"),
10576
10788
  paths: withDescription(stringArray, "[upsert_route/update_route] 路径列表,逗号分隔或 JSON 数组(1-32)"),
@@ -10605,7 +10817,7 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
10605
10817
  custom_hostname: optional(Type.String(), "[set_custom_hostname] 自定义域名")
10606
10818
  }, async (args) => {
10607
10819
  const resolveRef3 = (override) => {
10608
- const ref2 = override || projectRef;
10820
+ const ref2 = override || projectRef2;
10609
10821
  if (!ref2)
10610
10822
  throw new Error("'ref' is required for this action");
10611
10823
  return ref2;
@@ -10828,8 +11040,8 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
10828
11040
  }
10829
11041
 
10830
11042
  // src/shared/tools/branch-tools.ts
10831
- function resolveProjectRef(ref, projectRef) {
10832
- const resolved = typeof ref === "string" && ref.trim() ? ref.trim() : projectRef || "";
11043
+ function resolveProjectRef(ref, projectRef2) {
11044
+ const resolved = typeof ref === "string" && ref.trim() ? ref.trim() : projectRef2 || "";
10833
11045
  if (!resolved)
10834
11046
  throw new Error("'ref' is required for this action");
10835
11047
  return resolved;
@@ -11281,14 +11493,14 @@ async function executeMigrationPush(request, runtime) {
11281
11493
  const pushMigrations = runtime.getPushMigrations?.();
11282
11494
  if (!pushMigrations)
11283
11495
  return missingMigrationContextResult();
11284
- const projectRef = request.ref || runtime.projectRef;
11285
- if (!projectRef)
11496
+ const projectRef2 = request.ref || runtime.projectRef;
11497
+ if (!projectRef2)
11286
11498
  return missingProjectRefResult();
11287
11499
  const workdir = resolveExistingWorkdir(request.workdir, runtime.fallbackWorkdir);
11288
11500
  const migrationDirectory = resolve3(workdir, request.dir || "supabase/migrations");
11289
11501
  const migrationResponse = await pushMigrations({
11290
11502
  action: "push_migrations",
11291
- ref: projectRef,
11503
+ ref: projectRef2,
11292
11504
  dir: migrationDirectory,
11293
11505
  dry_run: request.dry_run
11294
11506
  });
@@ -12223,7 +12435,7 @@ var INVENTORY_MAX_BYTES = 1024 * 1024;
12223
12435
  var MUTATION_MAX_BYTES = 64 * 1024;
12224
12436
  var BACKUP_TIMEOUT_MS = 36 * 60000;
12225
12437
  var RELEASE_READ_RESPONSE_TIMEOUT_MS = 5000;
12226
- function isRecord(value) {
12438
+ function isRecord2(value) {
12227
12439
  return value !== null && typeof value === "object" && !Array.isArray(value);
12228
12440
  }
12229
12441
  function canonicalTimestamp3(value) {
@@ -12235,15 +12447,15 @@ function canonicalTimestamp3(value) {
12235
12447
  function validProjectRef(ref) {
12236
12448
  return SAFE_PROJECT_REF.test(ref);
12237
12449
  }
12238
- function backupBelongsToProject(backupId, projectRef) {
12239
- return BACKUP_ID.test(backupId) && backupId.startsWith(`logical-full_${projectRef}_`);
12450
+ function backupBelongsToProject(backupId, projectRef2) {
12451
+ return BACKUP_ID.test(backupId) && backupId.startsWith(`logical-full_${projectRef2}_`);
12240
12452
  }
12241
- function verifiedBackup(value, projectRef) {
12242
- if (!isRecord(value) || typeof value.backup_id !== "string" || !backupBelongsToProject(value.backup_id, projectRef) || value.project_ref !== projectRef || typeof value.database !== "string" || !SAFE_DATABASE.test(value.database) || value.kind !== "logical-full" || !canonicalTimestamp3(value.created_at) || !canonicalTimestamp3(value.completed_at) || new Date(value.completed_at).valueOf() < new Date(value.created_at).valueOf() || typeof value.bytes !== "number" || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || typeof value.sha256 !== "string" || !SHA256.test(value.sha256))
12453
+ function verifiedBackup(value, projectRef2) {
12454
+ if (!isRecord2(value) || typeof value.backup_id !== "string" || !backupBelongsToProject(value.backup_id, projectRef2) || value.project_ref !== projectRef2 || typeof value.database !== "string" || !SAFE_DATABASE.test(value.database) || value.kind !== "logical-full" || !canonicalTimestamp3(value.created_at) || !canonicalTimestamp3(value.completed_at) || new Date(value.completed_at).valueOf() < new Date(value.created_at).valueOf() || typeof value.bytes !== "number" || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || typeof value.sha256 !== "string" || !SHA256.test(value.sha256))
12243
12455
  return null;
12244
12456
  return {
12245
12457
  backup_id: value.backup_id,
12246
- project_ref: projectRef,
12458
+ project_ref: projectRef2,
12247
12459
  database: value.database,
12248
12460
  kind: "logical-full",
12249
12461
  created_at: value.created_at,
@@ -12252,10 +12464,10 @@ function verifiedBackup(value, projectRef) {
12252
12464
  sha256: value.sha256
12253
12465
  };
12254
12466
  }
12255
- function backupInventory(value, projectRef) {
12256
- if (!isRecord(value) || !Array.isArray(value.backups))
12467
+ function backupInventory(value, projectRef2) {
12468
+ if (!isRecord2(value) || !Array.isArray(value.backups))
12257
12469
  return null;
12258
- const backups = value.backups.map((backup) => verifiedBackup(backup, projectRef));
12470
+ const backups = value.backups.map((backup) => verifiedBackup(backup, projectRef2));
12259
12471
  if (backups.some((backup) => backup === null))
12260
12472
  return null;
12261
12473
  const inventory = backups;
@@ -12286,23 +12498,23 @@ function newlyCreatedBackup(before, after) {
12286
12498
  const additions = after.filter((backup) => !known.has(backup.backup_id));
12287
12499
  return additions.length === 1 ? additions[0] : null;
12288
12500
  }
12289
- function restoreRequest(projectRef, backupId, expectedSha256, restoreConfirmation) {
12290
- if (typeof backupId !== "string" || !backupBelongsToProject(backupId, projectRef)) {
12501
+ function restoreRequest(projectRef2, backupId, expectedSha256, restoreConfirmation) {
12502
+ if (typeof backupId !== "string" || !backupBelongsToProject(backupId, projectRef2)) {
12291
12503
  throw new Error("'backup_id' must identify a logical-full backup for 'ref'");
12292
12504
  }
12293
12505
  if (typeof expectedSha256 !== "string" || !SHA256.test(expectedSha256)) {
12294
12506
  throw new Error("'expected_sha256' must be a lowercase SHA-256 digest");
12295
12507
  }
12296
- const confirmation = `RESTORE_PROJECT:${projectRef}:${backupId}:${expectedSha256}`;
12508
+ const confirmation = `RESTORE_PROJECT:${projectRef2}:${backupId}:${expectedSha256}`;
12297
12509
  if (restoreConfirmation !== confirmation) {
12298
12510
  throw new Error("'restore_confirmation' must exactly confirm the selected logical backup restore");
12299
12511
  }
12300
12512
  return { backup_id: backupId, expected_sha256: expectedSha256, confirmation };
12301
12513
  }
12302
- function endpoint(projectRef) {
12303
- if (!validProjectRef(projectRef))
12514
+ function endpoint(projectRef2) {
12515
+ if (!validProjectRef(projectRef2))
12304
12516
  throw new Error("'ref' is invalid for release controls");
12305
- return `/v1/projects/${encodeURIComponent(projectRef)}`;
12517
+ return `/v1/projects/${encodeURIComponent(projectRef2)}`;
12306
12518
  }
12307
12519
  function httpFailure(operation, response) {
12308
12520
  if (response.responseReadError) {
@@ -12316,12 +12528,12 @@ function mutationFailure(operation, response) {
12316
12528
  }
12317
12529
  return releaseControlFailure(operation, "HTTP_ERROR", response.status);
12318
12530
  }
12319
- async function readInventory(http, projectRef) {
12320
- const response = await http.get(`${endpoint(projectRef)}/database/backups/logical`, {
12531
+ async function readInventory(http, projectRef2) {
12532
+ const response = await http.get(`${endpoint(projectRef2)}/database/backups/logical`, {
12321
12533
  maxJsonBytes: INVENTORY_MAX_BYTES,
12322
12534
  responseTimeoutMs: RELEASE_READ_RESPONSE_TIMEOUT_MS
12323
12535
  });
12324
- return { response, inventory: response.ok && response.status === 200 ? backupInventory(response.data, projectRef) : null };
12536
+ return { response, inventory: response.ok && response.status === 200 ? backupInventory(response.data, projectRef2) : null };
12325
12537
  }
12326
12538
  function readInventoryFailure(operation, read) {
12327
12539
  if (!read.response.ok)
@@ -12332,7 +12544,7 @@ function readInventoryFailure(operation, read) {
12332
12544
  return null;
12333
12545
  }
12334
12546
  function postgrestStatus(value) {
12335
- if (!isRecord(value) || value.component !== "postgrest" || !["running", "stopped"].includes(String(value.desired)) || !["running", "stopped", "starting", "error"].includes(String(value.actual)) || !["healthy", "unhealthy", "unknown"].includes(String(value.health)))
12547
+ if (!isRecord2(value) || value.component !== "postgrest" || !["running", "stopped"].includes(String(value.desired)) || !["running", "stopped", "starting", "error"].includes(String(value.actual)) || !["healthy", "unhealthy", "unknown"].includes(String(value.health)))
12336
12548
  return null;
12337
12549
  return {
12338
12550
  desired: value.desired,
@@ -12340,8 +12552,8 @@ function postgrestStatus(value) {
12340
12552
  health: value.health
12341
12553
  };
12342
12554
  }
12343
- async function readPostgrestStatus(http, projectRef) {
12344
- const response = await http.get(`${endpoint(projectRef)}/services/postgrest/status`, {
12555
+ async function readPostgrestStatus(http, projectRef2) {
12556
+ const response = await http.get(`${endpoint(projectRef2)}/services/postgrest/status`, {
12345
12557
  maxJsonBytes: MUTATION_MAX_BYTES,
12346
12558
  responseTimeoutMs: RELEASE_READ_RESPONSE_TIMEOUT_MS
12347
12559
  });
@@ -12353,7 +12565,7 @@ function readPostgrestFailure(operation, read) {
12353
12565
  return read.response.status === 200 && read.status ? null : releaseControlFailure(operation, "INVALID_RESPONSE", read.response.status);
12354
12566
  }
12355
12567
  function isRestartReceipt(value) {
12356
- return isRecord(value) && value.service === "postgrest" && value.action === "restart" && value.success === true;
12568
+ return isRecord2(value) && value.service === "postgrest" && value.action === "restart" && value.success === true;
12357
12569
  }
12358
12570
  function registerReleaseTools(server, http, options = {}) {
12359
12571
  server.tool("release", "Verified release controls using a Management API credential. Actions: logical_backup_list, logical_backup_create, logical_backup_restore, postgrest_status, postgrest_restart", {
@@ -12369,45 +12581,45 @@ function registerReleaseTools(server, http, options = {}) {
12369
12581
  expected_sha256: optional(Type.String(), "[logical_backup_restore] Exact lowercase SHA-256 from the selected project inventory"),
12370
12582
  restore_confirmation: optional(Type.String(), "[logical_backup_restore] Exact RESTORE_PROJECT:<ref>:<backup_id>:<sha256> confirmation")
12371
12583
  }, async ({ action, ref, backup_id, expected_sha256, restore_confirmation }) => {
12372
- const projectRef = typeof ref === "string" && ref || options.projectRef;
12373
- if (!projectRef)
12584
+ const projectRef2 = typeof ref === "string" && ref || options.projectRef;
12585
+ if (!projectRef2)
12374
12586
  throw new Error("'ref' is required for release controls");
12375
- if (!validProjectRef(projectRef))
12587
+ if (!validProjectRef(projectRef2))
12376
12588
  throw new Error("'ref' is invalid for release controls");
12377
12589
  if (action === "logical_backup_list") {
12378
- const read2 = await readInventory(http, projectRef);
12590
+ const read2 = await readInventory(http, projectRef2);
12379
12591
  const failure = readInventoryFailure("release.logical_backup.list", read2);
12380
12592
  return failure ?? releaseControlSuccess("release.logical_backup.list", {
12381
- project_ref: projectRef,
12593
+ project_ref: projectRef2,
12382
12594
  backups: read2.inventory.map(publicBackup)
12383
12595
  });
12384
12596
  }
12385
12597
  if (action === "logical_backup_create") {
12386
- const before = await readInventory(http, projectRef);
12598
+ const before = await readInventory(http, projectRef2);
12387
12599
  const beforeFailure = readInventoryFailure("release.logical_backup.create", before);
12388
12600
  if (beforeFailure)
12389
12601
  return beforeFailure;
12390
- const mutation2 = await http.postReleaseMutation(`${endpoint(projectRef)}/database/backups/logical`, {}, {
12602
+ const mutation2 = await http.postReleaseMutation(`${endpoint(projectRef2)}/database/backups/logical`, {}, {
12391
12603
  timeoutMs: BACKUP_TIMEOUT_MS
12392
12604
  });
12393
- const after = await readInventory(http, projectRef);
12605
+ const after = await readInventory(http, projectRef2);
12394
12606
  if (!mutation2.ok || mutation2.status !== 200) {
12395
12607
  return mutationFailure("release.logical_backup.create", mutation2);
12396
12608
  }
12397
- const responseBackup = isRecord(mutation2.data) ? verifiedBackup(mutation2.data.backup, projectRef) : null;
12609
+ const responseBackup = isRecord2(mutation2.data) ? verifiedBackup(mutation2.data.backup, projectRef2) : null;
12398
12610
  const afterFailure = readInventoryFailure("release.logical_backup.create", after);
12399
12611
  const addedBackup = after.inventory && newlyCreatedBackup(before.inventory, after.inventory);
12400
12612
  if (!responseBackup || afterFailure || !addedBackup || !equalBackup(responseBackup, addedBackup)) {
12401
12613
  return releaseControlFailure("release.logical_backup.create", "OUTCOME_UNKNOWN", mutation2.status);
12402
12614
  }
12403
12615
  return releaseControlSuccess("release.logical_backup.create", {
12404
- project_ref: projectRef,
12616
+ project_ref: projectRef2,
12405
12617
  backup: publicBackup(addedBackup)
12406
12618
  });
12407
12619
  }
12408
12620
  if (action === "logical_backup_restore") {
12409
- const request = restoreRequest(projectRef, backup_id, expected_sha256, restore_confirmation);
12410
- const before = await readInventory(http, projectRef);
12621
+ const request = restoreRequest(projectRef2, backup_id, expected_sha256, restore_confirmation);
12622
+ const before = await readInventory(http, projectRef2);
12411
12623
  const beforeFailure = readInventoryFailure("release.logical_backup.restore", before);
12412
12624
  if (beforeFailure)
12413
12625
  return beforeFailure;
@@ -12415,43 +12627,43 @@ function registerReleaseTools(server, http, options = {}) {
12415
12627
  if (!selectedBackup) {
12416
12628
  return releaseControlFailure("release.logical_backup.restore", "MUTATION_NOT_SUCCEEDED", null);
12417
12629
  }
12418
- const mutation2 = await http.postReleaseMutation(`${endpoint(projectRef)}/database/backups/logical/restore`, request, { timeoutMs: BACKUP_TIMEOUT_MS });
12630
+ const mutation2 = await http.postReleaseMutation(`${endpoint(projectRef2)}/database/backups/logical/restore`, request, { timeoutMs: BACKUP_TIMEOUT_MS });
12419
12631
  if (!mutation2.ok || mutation2.status !== 200) {
12420
12632
  return mutationFailure("release.logical_backup.restore", mutation2);
12421
12633
  }
12422
- const responseBackup = isRecord(mutation2.data) ? verifiedBackup(mutation2.data.restored_backup, projectRef) : null;
12423
- const after = await readInventory(http, projectRef);
12634
+ const responseBackup = isRecord2(mutation2.data) ? verifiedBackup(mutation2.data.restored_backup, projectRef2) : null;
12635
+ const after = await readInventory(http, projectRef2);
12424
12636
  const afterFailure = readInventoryFailure("release.logical_backup.restore", after);
12425
12637
  const restoredInventoryBackup = after.inventory?.find((backup) => backup.backup_id === request.backup_id);
12426
12638
  if (!responseBackup || !equalBackup(responseBackup, selectedBackup) || afterFailure || !restoredInventoryBackup || !equalBackup(restoredInventoryBackup, selectedBackup)) {
12427
12639
  return releaseControlFailure("release.logical_backup.restore", "OUTCOME_UNKNOWN", mutation2.status);
12428
12640
  }
12429
12641
  return releaseControlSuccess("release.logical_backup.restore", {
12430
- project_ref: projectRef,
12642
+ project_ref: projectRef2,
12431
12643
  backup: publicBackup(selectedBackup)
12432
12644
  });
12433
12645
  }
12434
12646
  if (action === "postgrest_status") {
12435
- const read2 = await readPostgrestStatus(http, projectRef);
12647
+ const read2 = await readPostgrestStatus(http, projectRef2);
12436
12648
  const failure = readPostgrestFailure("release.postgrest.status", read2);
12437
12649
  return failure ?? releaseControlSuccess("release.postgrest.status", {
12438
- project_ref: projectRef,
12650
+ project_ref: projectRef2,
12439
12651
  postgrest: read2.status
12440
12652
  });
12441
12653
  }
12442
12654
  if (action !== "postgrest_restart")
12443
12655
  throw new Error("Unknown release control action");
12444
- const mutation = await http.postReleaseMutation(`${endpoint(projectRef)}/services/postgrest/restart`);
12445
- const read = await readPostgrestStatus(http, projectRef);
12656
+ const mutation = await http.postReleaseMutation(`${endpoint(projectRef2)}/services/postgrest/restart`);
12657
+ const read = await readPostgrestStatus(http, projectRef2);
12446
12658
  if (!mutation.ok || mutation.status !== 200) {
12447
12659
  return mutationFailure("release.postgrest.restart", mutation);
12448
12660
  }
12449
- const readFailure = readPostgrestFailure("release.postgrest.restart", read);
12450
- if (!isRestartReceipt(mutation.data) || readFailure || read.status.desired !== "running" || read.status.actual !== "running" || read.status.health !== "healthy") {
12661
+ const readFailure2 = readPostgrestFailure("release.postgrest.restart", read);
12662
+ if (!isRestartReceipt(mutation.data) || readFailure2 || read.status.desired !== "running" || read.status.actual !== "running" || read.status.health !== "healthy") {
12451
12663
  return releaseControlFailure("release.postgrest.restart", "OUTCOME_UNKNOWN", mutation.status);
12452
12664
  }
12453
12665
  return releaseControlSuccess("release.postgrest.restart", {
12454
- project_ref: projectRef,
12666
+ project_ref: projectRef2,
12455
12667
  postgrest: read.status
12456
12668
  });
12457
12669
  });
@@ -12459,7 +12671,7 @@ function registerReleaseTools(server, http, options = {}) {
12459
12671
  // package.json
12460
12672
  var package_default = {
12461
12673
  name: "@supacloud/cli",
12462
- version: "0.25.0",
12674
+ version: "0.26.0",
12463
12675
  description: "Project-scoped CLI for SupaCloud users",
12464
12676
  type: "module",
12465
12677
  main: "./dist/index.js",
@@ -12801,7 +13013,7 @@ function createCliTools(context, confirmProduction) {
12801
13013
  ]
12802
13014
  })
12803
13015
  };
12804
- for (const name of ["database", "auth", "storage", "edge_functions", "secrets", "frontend", "queue", "task_events", "scheduled_functions", "mutations", "diagnostics", "gateway", "branch", "release"]) {
13016
+ for (const name of ["database", "auth", "oauth_clients", "storage", "edge_functions", "secrets", "frontend", "queue", "task_events", "scheduled_functions", "mutations", "diagnostics", "gateway", "branch", "release"]) {
12805
13017
  tools[name] = {
12806
13018
  schema: { action: genericActionSchema },
12807
13019
  callback: async () => ({
@@ -12878,6 +13090,7 @@ function createCliTools(context, confirmProduction) {
12878
13090
  pushMigrations = databaseTools.database?.callback;
12879
13091
  assign(databaseTools);
12880
13092
  assign(captureTools((server) => registerAuthTools(server, http)));
13093
+ assign(captureTools((server) => registerOAuthClientTools(server, http)));
12881
13094
  assign(captureTools((server) => registerStorageTools(server, http)));
12882
13095
  assign(captureTools((server) => registerAdvancedTools(server, http, process.env, {
12883
13096
  readOnly: context.readOnly
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",