@supacloud/cli 0.5.0 → 0.6.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 (3) hide show
  1. package/README.md +33 -0
  2. package/dist/index.js +535 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -30,6 +30,11 @@ Examples:
30
30
  supacloud-cli status
31
31
  supacloud-cli project get
32
32
  supacloud-cli project logs --log_type database
33
+ supacloud-cli project task_stats
34
+ supacloud-cli project task_detail --task_id task_123
35
+ supacloud-cli queue stats --queue emails
36
+ supacloud-cli queue dlq --queue emails --limit 20
37
+ supacloud-cli task_events inspect_webhook --ref abc123
33
38
  supacloud-cli database query --sql "select now()"
34
39
  supacloud-cli database query --ref abc123 --file ./queries/vector-search.sql
35
40
  supacloud-cli database push_migrations --ref abc123 --dir supabase/migrations --dry_run
@@ -62,5 +67,33 @@ Project commands owned by this CLI:
62
67
  - `project api_keys`
63
68
  - `project settings`
64
69
  - `project tasks`
70
+ - `project task_detail`
71
+ - `project task_stats`
72
+ - `project task_cancel`
73
+ - `project task_retry`
74
+ - `project dlq`
75
+ - `project background_settings`
76
+ - `project update_background_settings`
77
+
78
+ Queue commands:
79
+
80
+ - `queue send`
81
+ - `queue receive`
82
+ - `queue ack`
83
+ - `queue release`
84
+ - `queue fail`
85
+ - `queue retry`
86
+ - `queue delete_message`
87
+ - `queue list_messages`
88
+ - `queue stats`
89
+ - `queue dlq`
90
+ - `queue get_settings`
91
+ - `queue update_settings`
92
+
93
+ Task event commands:
94
+
95
+ - `task_events register_webhook`
96
+ - `task_events unregister_webhook`
97
+ - `task_events inspect_webhook`
65
98
 
66
99
  For server installation, SSH diagnostics, and tenant administration, use `@supacloud/admin`.
package/dist/index.js CHANGED
@@ -15399,7 +15399,7 @@ Actions: list_providers, get_provider, configure_provider, update_provider, disa
15399
15399
  url: exports_external.string().optional().describe("[configure] Custom OAuth URL"),
15400
15400
  app_id: exports_external.string().optional().describe("[wechat_*] WeChat App ID"),
15401
15401
  app_secret: exports_external.string().optional().describe("[wechat_*] WeChat App Secret"),
15402
- config: exports_external.record(exports_external.unknown()).optional().describe("[update_settings/update_config] Config fields")
15402
+ config: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("[update_settings/update_config] Config fields")
15403
15403
  }, async (args) => {
15404
15404
  const { action, ref, provider, client_id, client_secret, redirect_uri, url: url2, app_id, app_secret, config: config2 } = args;
15405
15405
  const need = (f) => {
@@ -15689,7 +15689,7 @@ Actions: list, deploy, deploy_bundle, config, source, delete, check`, {
15689
15689
  slug: exports_external.string().optional().describe("[deploy/deploy_bundle/config/source/delete/check] Function name"),
15690
15690
  code: exports_external.string().optional().describe("[deploy/check] Function source code (TypeScript)"),
15691
15691
  path: exports_external.string().optional().describe("[deploy/check] Local file path to read code from (alternative to code)"),
15692
- files: exports_external.record(exports_external.string()).optional().describe("[deploy_bundle] File map: { 'index.ts': '...', '_shared/x.ts': '...' }"),
15692
+ files: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("[deploy_bundle] File map: { 'index.ts': '...', '_shared/x.ts': '...' }"),
15693
15693
  entrypoint: exports_external.string().optional().describe("[deploy_bundle] Entrypoint file (default: index.ts)"),
15694
15694
  minify: exports_external.boolean().optional().describe("[deploy/deploy_bundle] Minify bundle"),
15695
15695
  verify_jwt: exports_external.boolean().optional().describe("[deploy/deploy_bundle/config] Set JWT verification for this function"),
@@ -15882,6 +15882,85 @@ ${JSON.stringify(r.data, null, 2)}` : `❌ Failed (${r.status})`;
15882
15882
  }
15883
15883
  return { content: [{ type: "text", text }] };
15884
15884
  });
15885
+ server.tool("task_events", `Task lifecycle webhook configuration.
15886
+ Actions: register_webhook, unregister_webhook, inspect_webhook`, {
15887
+ action: exports_external.enum(["register_webhook", "unregister_webhook", "inspect_webhook"]).describe("Action"),
15888
+ ref: exports_external.string().describe("Project ref"),
15889
+ url: exports_external.string().optional().describe("[register_webhook] HTTPS webhook URL for task lifecycle events"),
15890
+ secret: exports_external.string().optional().describe("[register_webhook] Optional HMAC secret for webhook verification")
15891
+ }, async (args) => {
15892
+ const { action, ref, url: url2, secret } = args;
15893
+ let text;
15894
+ switch (action) {
15895
+ case "register_webhook": {
15896
+ if (!url2)
15897
+ throw new Error("'url' is required for register_webhook");
15898
+ const body = { url: url2 };
15899
+ if (secret)
15900
+ body.secret = secret;
15901
+ const r = await http.post(`/v1/projects/${ref}/task-events/webhook`, body);
15902
+ text = r.ok ? `✅ Webhook registered for project ${ref}
15903
+ ${JSON.stringify(r.data, null, 2)}` : `❌ Failed (${r.status}): ${JSON.stringify(r.data)}`;
15904
+ break;
15905
+ }
15906
+ case "unregister_webhook": {
15907
+ const r = await http.delete(`/v1/projects/${ref}/task-events/webhook`);
15908
+ text = r.ok ? `✅ Webhook unregistered for project ${ref}` : `❌ Failed (${r.status}): ${JSON.stringify(r.data)}`;
15909
+ break;
15910
+ }
15911
+ case "inspect_webhook": {
15912
+ const r = await http.get(`/v1/projects/${ref}/task-events/webhook`);
15913
+ text = r.ok ? JSON.stringify(r.data, null, 2) : `❌ Failed (${r.status}): ${JSON.stringify(r.data)}`;
15914
+ break;
15915
+ }
15916
+ default:
15917
+ text = `❌ Unknown action`;
15918
+ }
15919
+ return { content: [{ type: "text", text }] };
15920
+ });
15921
+ server.tool("diagnostics", `Platform and project diagnostics: health checks, diagnostic runs, and repair.
15922
+ Actions: list_checks, run_checks, get_run, repair`, {
15923
+ action: exports_external.enum(["list_checks", "run_checks", "get_run", "repair"]).describe("Action"),
15924
+ ref: exports_external.string().optional().describe("Project ref (for project-scoped diagnostics)"),
15925
+ run_id: exports_external.string().optional().describe("[get_run/repair] Diagnostic run ID"),
15926
+ check_id: exports_external.string().optional().describe("[repair] Check result ID to repair")
15927
+ }, async (args) => {
15928
+ const { action, ref, run_id, check_id } = args;
15929
+ let text;
15930
+ switch (action) {
15931
+ case "list_checks": {
15932
+ const path = ref ? `/v1/projects/${ref}/diagnostics/checks` : "/v1/diagnostics/checks";
15933
+ text = JSON.stringify((await http.get(path)).data, null, 2);
15934
+ break;
15935
+ }
15936
+ case "run_checks": {
15937
+ const path = ref ? `/v1/projects/${ref}/diagnostics/runs` : "/v1/diagnostics/runs";
15938
+ const r = await http.post(path);
15939
+ text = r.ok ? `✅ Diagnostic run started
15940
+ ${JSON.stringify(r.data, null, 2)}` : `❌ Failed (${r.status}): ${JSON.stringify(r.data)}`;
15941
+ break;
15942
+ }
15943
+ case "get_run": {
15944
+ if (!run_id)
15945
+ throw new Error("'run_id' is required for get_run");
15946
+ const path = ref ? `/v1/projects/${ref}/diagnostics/runs/${run_id}` : `/v1/diagnostics/runs/${run_id}`;
15947
+ text = JSON.stringify((await http.get(path)).data, null, 2);
15948
+ break;
15949
+ }
15950
+ case "repair": {
15951
+ if (!check_id)
15952
+ throw new Error("'check_id' is required for repair");
15953
+ const path = `/v1/diagnostics/results/${check_id}/repair`;
15954
+ const r = await http.post(path);
15955
+ text = r.ok ? `✅ Repair executed for ${check_id}
15956
+ ${JSON.stringify(r.data, null, 2)}` : `❌ Failed (${r.status}): ${JSON.stringify(r.data)}`;
15957
+ break;
15958
+ }
15959
+ default:
15960
+ text = `❌ Unknown action`;
15961
+ }
15962
+ return { content: [{ type: "text", text }] };
15963
+ });
15885
15964
  }
15886
15965
 
15887
15966
  // src/shared/tools/frontend-tools.ts
@@ -15915,7 +15994,7 @@ Actions: list, get, create, update, delete, deploy_git, deploy_upload, redeploy,
15915
15994
  output_dir: exports_external.string().optional().describe("[create/update] Output directory override"),
15916
15995
  install_command: exports_external.string().optional().describe("[create/update] Install command override"),
15917
15996
  node_version: exports_external.string().optional().describe("[create/update] Node.js version"),
15918
- env_vars: exports_external.record(exports_external.string()).optional().describe("[create/update/set_env] Environment variables"),
15997
+ env_vars: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("[create/update/set_env] Environment variables"),
15919
15998
  git_url: exports_external.string().optional().describe("[deploy_git] Git repository URL"),
15920
15999
  branch: exports_external.string().optional().describe("[deploy_git] Branch (default: main)"),
15921
16000
  zip_path: exports_external.string().optional().describe("[deploy_upload] Local zip file path")
@@ -16038,19 +16117,32 @@ var formatTasks = (data) => {
16038
16117
  return JSON.stringify(data, null, 2);
16039
16118
  if (data.length === 0)
16040
16119
  return "No tasks found.";
16041
- const emoji3 = { pending: "⏳", processing: "\uD83D\uDD04", completed: "✅", failed: "❌" };
16120
+ const emoji3 = {
16121
+ pending: "⏳",
16122
+ leased: "\uD83D\uDD13",
16123
+ running: "\uD83D\uDD04",
16124
+ retry_scheduled: "\uD83D\uDD01",
16125
+ succeeded: "✅",
16126
+ failed: "❌",
16127
+ dead_lettered: "\uD83D\uDC80",
16128
+ cancelled: "\uD83D\uDEAB",
16129
+ queued: "\uD83D\uDCE5",
16130
+ processing: "\uD83D\uDD04",
16131
+ completed: "✅"
16132
+ };
16042
16133
  let out = `\uD83D\uDCCB Tasks (${data.length}):
16043
16134
 
16044
16135
  `;
16045
16136
  for (const t of data) {
16046
- out += ` ${emoji3[t.status] || ""} ${t.task_type} — ${t.status}
16137
+ const st = t.status || "?";
16138
+ out += ` ${emoji3[st] || "❓"} ${t.task_type || t.type || ""} — ${st}
16047
16139
  ID: ${t.id}
16048
16140
  `;
16049
- if (t.retries > 0)
16050
- out += ` Retries: ${t.retries}
16141
+ if (t.retries > 0 || t.retry_count > 0)
16142
+ out += ` Retries: ${t.retries || t.retry_count}
16051
16143
  `;
16052
- if (t.error)
16053
- out += ` Error: ${t.error}
16144
+ if (t.error || t.error_message)
16145
+ out += ` Error: ${t.error || t.error_message}
16054
16146
  `;
16055
16147
  if (t.created_at)
16056
16148
  out += ` Created: ${t.created_at}
@@ -16060,6 +16152,77 @@ var formatTasks = (data) => {
16060
16152
  }
16061
16153
  return out;
16062
16154
  };
16155
+ var formatTaskDetail = (data) => {
16156
+ if (!data || typeof data !== "object")
16157
+ return JSON.stringify(data, null, 2);
16158
+ const t = data;
16159
+ const emoji3 = {
16160
+ pending: "⏳",
16161
+ leased: "\uD83D\uDD13",
16162
+ running: "\uD83D\uDD04",
16163
+ retry_scheduled: "\uD83D\uDD01",
16164
+ succeeded: "✅",
16165
+ failed: "❌",
16166
+ dead_lettered: "\uD83D\uDC80",
16167
+ cancelled: "\uD83D\uDEAB"
16168
+ };
16169
+ const st = String(t.status || "?");
16170
+ const lines = [
16171
+ `${emoji3[st] || "❓"} Task Detail:`,
16172
+ ` ID: ${t.id}`,
16173
+ ` Type: ${t.task_type || t.type || "?"}`,
16174
+ ` Status: ${st}`,
16175
+ ` Attempt: ${t.attempt ?? "?"}/${t.max_attempts ?? "?"}`,
16176
+ ` Function: ${t.function_slug || "-"}`,
16177
+ ` Created: ${t.created_at || "?"}`,
16178
+ ` Updated: ${t.updated_at || "-"}`
16179
+ ];
16180
+ if (t.error || t.error_message)
16181
+ lines.push(` Error: ${t.error || t.error_message}`);
16182
+ if (t.correlation_id)
16183
+ lines.push(` Correlation: ${t.correlation_id}`);
16184
+ if (t.business_task_id)
16185
+ lines.push(` Business ID: ${t.business_task_id}`);
16186
+ if (t.trace_id)
16187
+ lines.push(` Trace: ${t.trace_id}`);
16188
+ if (t.result)
16189
+ lines.push(` Result: ${JSON.stringify(t.result).slice(0, 200)}`);
16190
+ const attempts = t.attempts;
16191
+ if (Array.isArray(attempts) && attempts.length > 0) {
16192
+ lines.push("", ` Attempts (${attempts.length}):`);
16193
+ for (const a of attempts) {
16194
+ const aa = a;
16195
+ lines.push(` #${aa.attempt_no ?? "?"} ${aa.status || ""} ${aa.error ? `— ${aa.error}` : ""}`);
16196
+ }
16197
+ }
16198
+ const logs = t.latest_logs;
16199
+ if (Array.isArray(logs) && logs.length > 0) {
16200
+ lines.push("", ` Latest Logs (${logs.length}):`);
16201
+ for (const l of logs.slice(-10)) {
16202
+ const ll = l;
16203
+ lines.push(` [${ll.stream || "?"}] ${String(ll.message || "").slice(0, 200)}`);
16204
+ }
16205
+ }
16206
+ return lines.join(`
16207
+ `);
16208
+ };
16209
+ var formatTaskStats = (data) => {
16210
+ if (!data || typeof data !== "object")
16211
+ return JSON.stringify(data, null, 2);
16212
+ const s = data;
16213
+ return [
16214
+ "\uD83D\uDCCA Task Stats:",
16215
+ ` Pending: ${s.pending ?? "?"}`,
16216
+ ` Leased: ${s.leased ?? "?"}`,
16217
+ ` Running: ${s.running ?? "?"}`,
16218
+ ` Retry Scheduled: ${s.retry_scheduled ?? "?"}`,
16219
+ ` Succeeded: ${s.succeeded ?? "?"}`,
16220
+ ` Failed: ${s.failed ?? "?"}`,
16221
+ ` Dead Lettered: ${s.dead_lettered ?? "?"}`,
16222
+ ` Cancelled: ${s.cancelled ?? "?"}`
16223
+ ].join(`
16224
+ `);
16225
+ };
16063
16226
  var ok = (res) => res.ok ? JSON.stringify(res.data, null, 2) : `❌ Failed (${res.status}): ${JSON.stringify(res.data)}`;
16064
16227
  function buildProjectLogsPath(ref, logType) {
16065
16228
  const params = new URLSearchParams({ limit: "200" });
@@ -16076,11 +16239,29 @@ function resolveRef2(refFromArgs, defaultRef) {
16076
16239
  }
16077
16240
  function registerUserProjectCliTools(server, http, options = {}) {
16078
16241
  const { projectRef } = options;
16079
- server.tool("project", "Project-scoped inspection and developer operations. Actions: get, health, logs, api_keys, settings, tasks", {
16080
- action: exports_external.enum(["get", "health", "logs", "api_keys", "settings", "tasks"]).describe("Action to perform"),
16242
+ server.tool("project", `Project-scoped inspection and developer operations.
16243
+ Actions: get, health, logs, api_keys, settings, tasks, task_detail, task_cancel, task_retry, task_stats, dlq, background_settings, update_background_settings`, {
16244
+ action: exports_external.enum([
16245
+ "get",
16246
+ "health",
16247
+ "logs",
16248
+ "api_keys",
16249
+ "settings",
16250
+ "tasks",
16251
+ "task_detail",
16252
+ "task_cancel",
16253
+ "task_retry",
16254
+ "task_stats",
16255
+ "dlq",
16256
+ "background_settings"
16257
+ ]).describe("Action to perform"),
16081
16258
  ref: exports_external.string().optional().describe(projectRef ? "Optional override when not auto-linked" : "Project ref"),
16082
- log_type: exports_external.enum(["all", "auth", "database", "api"]).optional().describe("[logs] Filter by service")
16083
- }, async ({ action, ref, log_type }) => {
16259
+ log_type: exports_external.enum(["all", "auth", "database", "api"]).optional().describe("[logs] Filter by service"),
16260
+ task_id: exports_external.string().optional().describe("[task_detail/task_cancel/task_retry] Task ID"),
16261
+ limit: exports_external.number().optional().describe("[tasks/dlq] Max items to return"),
16262
+ concurrency: exports_external.number().optional().describe("[update_background_settings] Max concurrent background tasks"),
16263
+ max_attempts: exports_external.number().optional().describe("[update_background_settings] Max attempts for background tasks")
16264
+ }, async ({ action, ref, log_type, task_id, limit, concurrency, max_attempts }) => {
16084
16265
  const resolvedRef = resolveRef2(ref, projectRef);
16085
16266
  let text;
16086
16267
  switch (action) {
@@ -16102,10 +16283,68 @@ function registerUserProjectCliTools(server, http, options = {}) {
16102
16283
  text = ok(await http.get(`/v1/projects/${resolvedRef}/settings`));
16103
16284
  break;
16104
16285
  case "tasks": {
16105
- const res = await http.get(`/v1/projects/${resolvedRef}/tasks`);
16286
+ const params = {};
16287
+ if (limit)
16288
+ params.limit = String(limit);
16289
+ const qs = Object.keys(params).length ? `?${new URLSearchParams(params)}` : "";
16290
+ const res = await http.get(`/v1/projects/${resolvedRef}/tasks${qs}`);
16106
16291
  text = res.ok ? formatTasks(res.data) : `❌ Failed (${res.status})`;
16107
16292
  break;
16108
16293
  }
16294
+ case "task_detail": {
16295
+ if (!task_id)
16296
+ throw new Error("'task_id' is required for task_detail");
16297
+ const res = await http.get(`/v1/projects/${resolvedRef}/tasks/${task_id}`);
16298
+ text = res.ok ? formatTaskDetail(res.data) : `❌ Failed (${res.status})`;
16299
+ break;
16300
+ }
16301
+ case "task_cancel": {
16302
+ if (!task_id)
16303
+ throw new Error("'task_id' is required for task_cancel");
16304
+ const res = await http.post(`/v1/projects/${resolvedRef}/tasks/${task_id}/cancel`);
16305
+ text = res.ok ? `✅ Task ${task_id} cancelled` : `❌ Failed (${res.status})`;
16306
+ break;
16307
+ }
16308
+ case "task_retry": {
16309
+ if (!task_id)
16310
+ throw new Error("'task_id' is required for task_retry");
16311
+ const res = await http.post(`/v1/projects/${resolvedRef}/tasks/${task_id}/retry`);
16312
+ text = res.ok ? `✅ Task ${task_id} queued for retry
16313
+ ${JSON.stringify(res.data, null, 2)}` : `❌ Failed (${res.status})`;
16314
+ break;
16315
+ }
16316
+ case "task_stats": {
16317
+ const res = await http.get(`/v1/projects/${resolvedRef}/tasks/stats`);
16318
+ text = res.ok ? formatTaskStats(res.data) : `❌ Failed (${res.status})`;
16319
+ break;
16320
+ }
16321
+ case "dlq": {
16322
+ const params = {};
16323
+ if (limit)
16324
+ params.limit = String(limit);
16325
+ const qs = Object.keys(params).length ? `?${new URLSearchParams(params)}` : "";
16326
+ const res = await http.get(`/v1/projects/${resolvedRef}/tasks/dlq${qs}`);
16327
+ text = res.ok ? formatTasks(res.data) : `❌ Failed (${res.status})`;
16328
+ break;
16329
+ }
16330
+ case "background_settings": {
16331
+ const res = await http.get(`/v1/projects/${resolvedRef}/tasks/settings/background`);
16332
+ text = res.ok ? JSON.stringify(res.data, null, 2) : `❌ Failed (${res.status})`;
16333
+ break;
16334
+ }
16335
+ case "update_background_settings": {
16336
+ const body = {};
16337
+ if (concurrency)
16338
+ body.concurrency = concurrency;
16339
+ if (max_attempts)
16340
+ body.max_attempts = max_attempts;
16341
+ if (Object.keys(body).length === 0)
16342
+ throw new Error("At least one setting (concurrency or max_attempts) is required");
16343
+ const res = await http.patch(`/v1/projects/${resolvedRef}/tasks/settings/background`, body);
16344
+ text = res.ok ? `✅ Background settings updated
16345
+ ${JSON.stringify(res.data, null, 2)}` : `❌ Failed (${res.status})`;
16346
+ break;
16347
+ }
16109
16348
  default:
16110
16349
  text = `❌ Unknown action: ${action}`;
16111
16350
  }
@@ -16113,11 +16352,285 @@ function registerUserProjectCliTools(server, http, options = {}) {
16113
16352
  });
16114
16353
  }
16115
16354
 
16355
+ // src/shared/tools/queue-tools.ts
16356
+ function formatQueueStats(data) {
16357
+ if (!data || typeof data !== "object")
16358
+ return JSON.stringify(data, null, 2);
16359
+ const s = data;
16360
+ return [
16361
+ "\uD83D\uDCCA Queue Stats:",
16362
+ ` Pending: ${s.pending ?? "?"}`,
16363
+ ` Leased: ${s.leased ?? "?"}`,
16364
+ ` Running: ${s.running ?? "?"}`,
16365
+ ` Retry Scheduled: ${s.retryScheduled ?? s.retry_scheduled ?? "?"}`,
16366
+ ` Succeeded (24h): ${s.succeededLast24h ?? s.succeeded_last_24h ?? "?"}`,
16367
+ ` Failed (24h): ${s.failedLast24h ?? s.failed_last_24h ?? "?"}`,
16368
+ ` Dead Lettered: ${s.deadLettered ?? s.dead_lettered ?? "?"}`,
16369
+ ` In Flight: ${s.inFlight ?? s.in_flight ?? "?"}`,
16370
+ ` Oldest Pending: ${s.oldestPendingAgeSec ?? s.oldest_pending_age_sec ?? "?"}s`
16371
+ ].join(`
16372
+ `);
16373
+ }
16374
+ function formatQueueSettings(data) {
16375
+ if (!data || typeof data !== "object")
16376
+ return JSON.stringify(data, null, 2);
16377
+ const s = data;
16378
+ return [
16379
+ "⚙️ Queue Settings:",
16380
+ ` Max In Flight: ${s.max_in_flight ?? "?"}`,
16381
+ ` Visibility Timeout: ${s.default_visibility_timeout_sec ?? "?"}s`,
16382
+ ` Max Attempts: ${s.max_attempts ?? "?"}`,
16383
+ ` Rate Limit: ${s.rate_limit_per_minute ?? "?"}/min`
16384
+ ].join(`
16385
+ `);
16386
+ }
16387
+ function formatMessages(data, label = "Messages") {
16388
+ if (!Array.isArray(data))
16389
+ return JSON.stringify(data, null, 2);
16390
+ if (data.length === 0)
16391
+ return `No ${label.toLowerCase()} found.`;
16392
+ const emoji3 = {
16393
+ pending: "⏳",
16394
+ leased: "\uD83D\uDD13",
16395
+ running: "\uD83D\uDD04",
16396
+ retry_scheduled: "\uD83D\uDD01",
16397
+ succeeded: "✅",
16398
+ failed: "❌",
16399
+ dead_lettered: "\uD83D\uDC80"
16400
+ };
16401
+ let out = `${label} (${data.length}):
16402
+
16403
+ `;
16404
+ for (const m of data) {
16405
+ const st = m.status || "?";
16406
+ out += ` ${emoji3[st] || "❓"} ${st} — id: ${m.id}
16407
+ `;
16408
+ if (m.attempt != null)
16409
+ out += ` Attempt: ${m.attempt}/${m.max_attempts ?? "?"}
16410
+ `;
16411
+ if (m.error)
16412
+ out += ` Error: ${typeof m.error === "string" ? m.error : JSON.stringify(m.error)}
16413
+ `;
16414
+ if (m.created_at)
16415
+ out += ` Created: ${m.created_at}
16416
+ `;
16417
+ out += `
16418
+ `;
16419
+ }
16420
+ return out;
16421
+ }
16422
+ function resolveRef3(refFromArgs, defaultRef) {
16423
+ const ref = defaultRef || refFromArgs;
16424
+ if (!ref)
16425
+ throw new Error("'ref' is required for this action");
16426
+ return ref;
16427
+ }
16428
+ function registerQueueTools(server, http, options = {}) {
16429
+ const { projectRef } = options;
16430
+ server.tool("queue", `Message queue operations for task-based messaging.
16431
+ Actions: list, stats, list_messages, dlq, get_message, send, receive, ack, release, fail, retry, delete_message, get_settings, update_settings`, {
16432
+ action: exports_external.enum([
16433
+ "list",
16434
+ "stats",
16435
+ "list_messages",
16436
+ "dlq",
16437
+ "get_message",
16438
+ "send",
16439
+ "receive",
16440
+ "ack",
16441
+ "release",
16442
+ "fail",
16443
+ "retry",
16444
+ "delete_message",
16445
+ "get_settings",
16446
+ "update_settings"
16447
+ ]).describe("Action"),
16448
+ ref: exports_external.string().optional().describe("Project ref"),
16449
+ queue: exports_external.string().optional().describe("[list/stats/list_messages/dlq/get_message/send/receive/ack/release/fail/retry/delete_message/get_settings/update_settings] Queue name"),
16450
+ message_id: exports_external.string().optional().describe("[get_message/ack/release/fail/retry/delete_message] Message ID"),
16451
+ payload: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("[send] Message payload"),
16452
+ delay_ms: exports_external.number().optional().describe("[send] Delay in ms before message becomes visible"),
16453
+ max_attempts: exports_external.number().optional().describe("[send] Max delivery attempts"),
16454
+ idempotency_key: exports_external.string().optional().describe("[send] Idempotency key for dedup"),
16455
+ correlation_id: exports_external.string().optional().describe("[send] Correlation ID for tracing"),
16456
+ business_task_id: exports_external.string().optional().describe("[send] Business task ID for cross-system mapping"),
16457
+ metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("[send] Arbitrary metadata attached to the message"),
16458
+ visibility_timeout_sec: exports_external.number().optional().describe("[receive] Visibility timeout in seconds"),
16459
+ status: exports_external.string().optional().describe("[list_messages] Filter by status (comma-separated)"),
16460
+ limit: exports_external.number().optional().describe("[list_messages/dlq] Max messages to return"),
16461
+ result: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("[ack] Ack result payload"),
16462
+ error: exports_external.string().optional().describe("[release/fail] Error description"),
16463
+ delay_ms_release: exports_external.number().optional().describe("[release] Delay before message becomes visible again"),
16464
+ max_in_flight: exports_external.number().optional().describe("[update_settings] Max concurrent in-flight messages"),
16465
+ default_visibility_timeout: exports_external.number().optional().describe("[update_settings] Default visibility timeout (sec)"),
16466
+ max_attempts_setting: exports_external.number().optional().describe("[update_settings] Max delivery attempts"),
16467
+ rate_limit: exports_external.number().optional().describe("[update_settings] Rate limit per minute")
16468
+ }, async (args) => {
16469
+ const resolvedRef = resolveRef3(args.ref, projectRef);
16470
+ const q = args.queue;
16471
+ const need = (fields) => {
16472
+ for (const f of fields) {
16473
+ if (!args[f])
16474
+ throw new Error(`'${f}' is required for '${args.action}'`);
16475
+ }
16476
+ };
16477
+ const qBase = `/v1/projects/${resolvedRef}/tasks/queues/${q}`;
16478
+ let text;
16479
+ switch (args.action) {
16480
+ case "list": {
16481
+ need(["queue"]);
16482
+ const res = await http.get(`${qBase}/messages`);
16483
+ text = res.ok ? formatMessages(res.data, `Queue ${q}`) : `❌ Failed (${res.status})`;
16484
+ break;
16485
+ }
16486
+ case "stats": {
16487
+ need(["queue"]);
16488
+ const res = await http.get(`${qBase}/stats`);
16489
+ text = res.ok ? formatQueueStats(res.data) : `❌ Failed (${res.status})`;
16490
+ break;
16491
+ }
16492
+ case "list_messages": {
16493
+ need(["queue"]);
16494
+ const params = {};
16495
+ if (args.status)
16496
+ params.status = args.status;
16497
+ if (args.limit)
16498
+ params.limit = String(args.limit);
16499
+ const qs = Object.keys(params).length ? `?${new URLSearchParams(params)}` : "";
16500
+ const res = await http.get(`${qBase}/messages${qs}`);
16501
+ text = res.ok ? formatMessages(res.data) : `❌ Failed (${res.status})`;
16502
+ break;
16503
+ }
16504
+ case "dlq": {
16505
+ need(["queue"]);
16506
+ const params = { status: "dead_lettered" };
16507
+ if (args.limit)
16508
+ params.limit = String(args.limit);
16509
+ const res = await http.get(`${qBase}/messages?${new URLSearchParams(params)}`);
16510
+ text = res.ok ? formatMessages(res.data, "Dead-Letter Messages") : `❌ Failed (${res.status})`;
16511
+ break;
16512
+ }
16513
+ case "get_message": {
16514
+ need(["queue", "message_id"]);
16515
+ const res = await http.get(`${qBase}/messages/${args.message_id}`);
16516
+ text = res.ok ? JSON.stringify(res.data, null, 2) : `❌ Failed (${res.status})`;
16517
+ break;
16518
+ }
16519
+ case "send": {
16520
+ need(["queue", "payload"]);
16521
+ const body = {
16522
+ payload: args.payload,
16523
+ ...args.delay_ms ? { delayMs: args.delay_ms } : {},
16524
+ ...args.max_attempts ? { maxAttempts: args.max_attempts } : {},
16525
+ ...args.idempotency_key ? { idempotencyKey: args.idempotency_key } : {},
16526
+ ...args.correlation_id ? { correlationId: args.correlation_id } : {},
16527
+ ...args.business_task_id ? { businessTaskId: args.business_task_id } : {},
16528
+ ...args.metadata ? { metadata: args.metadata } : {}
16529
+ };
16530
+ const res = await http.post(`${qBase}/messages`, body);
16531
+ text = res.ok ? `✅ Message sent
16532
+ ${JSON.stringify(res.data, null, 2)}` : `❌ Failed (${res.status}): ${JSON.stringify(res.data)}`;
16533
+ break;
16534
+ }
16535
+ case "receive": {
16536
+ need(["queue"]);
16537
+ const body = {};
16538
+ if (args.visibility_timeout_sec)
16539
+ body.visibilityTimeoutSec = args.visibility_timeout_sec;
16540
+ const res = await http.post(`${qBase}/messages/receive`, body);
16541
+ if (!res.ok) {
16542
+ text = `❌ Failed (${res.status})`;
16543
+ break;
16544
+ }
16545
+ text = res.data ? JSON.stringify(res.data, null, 2) : "\uD83D\uDCED No messages available";
16546
+ break;
16547
+ }
16548
+ case "ack": {
16549
+ need(["queue", "message_id"]);
16550
+ const body = args.result ? { result: args.result } : {};
16551
+ const res = await http.post(`${qBase}/messages/${args.message_id}/ack`, body);
16552
+ text = res.ok ? `✅ Message ${args.message_id} acknowledged` : `❌ Failed (${res.status})`;
16553
+ break;
16554
+ }
16555
+ case "release": {
16556
+ need(["queue", "message_id"]);
16557
+ const body = {};
16558
+ if (args.delay_ms_release)
16559
+ body.delayMs = args.delay_ms_release;
16560
+ if (args.error)
16561
+ body.error = args.error;
16562
+ const res = await http.post(`${qBase}/messages/${args.message_id}/release`, body);
16563
+ text = res.ok ? `✅ Message ${args.message_id} released back to queue` : `❌ Failed (${res.status})`;
16564
+ break;
16565
+ }
16566
+ case "fail": {
16567
+ need(["queue", "message_id"]);
16568
+ const body = args.error ? { error: args.error } : {};
16569
+ const res = await http.post(`${qBase}/messages/${args.message_id}/fail`, body);
16570
+ text = res.ok ? `✅ Message ${args.message_id} marked as failed` : `❌ Failed (${res.status})`;
16571
+ break;
16572
+ }
16573
+ case "retry": {
16574
+ need(["queue", "message_id"]);
16575
+ const res = await http.post(`${qBase}/messages/${args.message_id}/retry`);
16576
+ text = res.ok ? `✅ Message ${args.message_id} queued for retry` : `❌ Failed (${res.status})`;
16577
+ break;
16578
+ }
16579
+ case "delete_message": {
16580
+ need(["queue", "message_id"]);
16581
+ const res = await http.delete(`${qBase}/messages/${args.message_id}`);
16582
+ text = res.ok ? `✅ Message ${args.message_id} deleted` : `❌ Failed (${res.status})`;
16583
+ break;
16584
+ }
16585
+ case "get_settings": {
16586
+ need(["queue"]);
16587
+ const res = await http.get(`${qBase}/settings`);
16588
+ text = res.ok ? formatQueueSettings(res.data) : `❌ Failed (${res.status})`;
16589
+ break;
16590
+ }
16591
+ case "update_settings": {
16592
+ need(["queue"]);
16593
+ const body = {};
16594
+ if (args.max_in_flight)
16595
+ body.max_in_flight = args.max_in_flight;
16596
+ if (args.default_visibility_timeout)
16597
+ body.default_visibility_timeout_sec = args.default_visibility_timeout;
16598
+ if (args.max_attempts_setting)
16599
+ body.max_attempts = args.max_attempts_setting;
16600
+ if (args.rate_limit)
16601
+ body.rate_limit_per_minute = args.rate_limit;
16602
+ if (Object.keys(body).length === 0)
16603
+ throw new Error("At least one setting field is required");
16604
+ const res = await http.patch(`${qBase}/settings`, body);
16605
+ text = res.ok ? `✅ Settings updated
16606
+ ${formatQueueSettings(res.data)}` : `❌ Failed (${res.status})`;
16607
+ break;
16608
+ }
16609
+ default:
16610
+ text = `❌ Unknown action: ${args.action}`;
16611
+ }
16612
+ return { content: [{ type: "text", text }] };
16613
+ });
16614
+ }
16615
+
16116
16616
  // src/index.ts
16117
16617
  var invokedCommand = path.basename(process.argv[1] || "supacloud-cli");
16118
16618
  var commandName = invokedCommand === "supacloud" ? "supacloud" : "supacloud-cli";
16119
16619
  var preferredCommand = "supacloud-cli";
16120
- var projectActionSchema = exports_external.enum(["get", "health", "logs", "api_keys", "settings", "tasks"]);
16620
+ var projectActionSchema = exports_external.enum([
16621
+ "get",
16622
+ "health",
16623
+ "logs",
16624
+ "api_keys",
16625
+ "settings",
16626
+ "tasks",
16627
+ "task_detail",
16628
+ "task_cancel",
16629
+ "task_retry",
16630
+ "task_stats",
16631
+ "dlq",
16632
+ "background_settings"
16633
+ ]);
16121
16634
  var genericActionSchema = exports_external.string();
16122
16635
  function unwrapMcpSchema(schema) {
16123
16636
  if (schema && typeof schema === "object" && !Array.isArray(schema) && "args" in schema) {
@@ -16172,6 +16685,9 @@ EXAMPLES
16172
16685
  ${preferredCommand} status
16173
16686
  ${preferredCommand} project get
16174
16687
  ${preferredCommand} project logs --log_type database
16688
+ ${preferredCommand} project task_stats
16689
+ ${preferredCommand} queue stats --queue emails
16690
+ ${preferredCommand} queue dlq --queue emails --limit 20
16175
16691
  ${preferredCommand} frontend list --ref abc123
16176
16692
  ${preferredCommand} database query --sql "select now()"
16177
16693
  ${preferredCommand} database query --ref abc123 --file ./queries/vector-search.sql
@@ -16232,7 +16748,7 @@ function createCliTools() {
16232
16748
  ]
16233
16749
  })
16234
16750
  };
16235
- for (const name of ["database", "auth", "storage", "edge_functions", "secrets", "frontend"]) {
16751
+ for (const name of ["database", "auth", "storage", "edge_functions", "secrets", "frontend", "queue", "task_events", "diagnostics"]) {
16236
16752
  tools[name] = {
16237
16753
  schema: { action: genericActionSchema },
16238
16754
  callback: async () => ({
@@ -16295,6 +16811,9 @@ function createCliTools() {
16295
16811
  assign(captureTools((server) => registerStorageTools(server, http)));
16296
16812
  assign(captureTools((server) => registerAdvancedTools(server, http)));
16297
16813
  assign(captureTools((server) => registerFrontendTools(server, http)));
16814
+ assign(captureTools((server) => registerQueueTools(server, http, {
16815
+ projectRef: context.projectRef || undefined
16816
+ })));
16298
16817
  delete tools.platform;
16299
16818
  return tools;
16300
16819
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",