@supacloud/cli 0.5.0 → 0.6.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/README.md +33 -0
- package/dist/index.js +535 -16
- 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 = {
|
|
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
|
-
|
|
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",
|
|
16080
|
-
|
|
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
|
-
|
|
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
|
|
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([
|
|
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
|
}
|