@stacksjs/actions 0.70.88 → 0.70.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action.d.ts +149 -0
- package/dist/action.js +78 -0
- package/dist/actions.d.ts +2 -0
- package/dist/actions.js +2 -0
- package/dist/add.d.ts +6 -0
- package/dist/add.js +20 -0
- package/dist/ai/request-model-access.d.ts +1 -0
- package/dist/ai/request-model-access.js +10 -0
- package/dist/auth/client.d.ts +1 -0
- package/dist/auth/client.js +44 -0
- package/dist/auth/prune.d.ts +1 -0
- package/dist/auth/prune.js +35 -0
- package/dist/auth/setup.d.ts +1 -0
- package/dist/auth/setup.js +322 -0
- package/dist/auth/token.d.ts +1 -0
- package/dist/auth/token.js +10 -0
- package/dist/blog-admin.d.ts +55 -0
- package/dist/blog-admin.js +156 -0
- package/dist/blog.d.ts +30 -0
- package/dist/blog.js +398 -0
- package/dist/build/cli.d.ts +1 -0
- package/dist/build/cli.js +6 -0
- package/dist/build/component-libs.d.ts +1 -0
- package/dist/build/component-libs.js +7 -0
- package/dist/build/core.d.ts +1 -0
- package/dist/build/core.js +55 -0
- package/dist/build/desktop.d.ts +1 -0
- package/dist/build/desktop.js +5 -0
- package/dist/build/docs.d.ts +1 -0
- package/dist/build/docs.js +5 -0
- package/dist/build/frontend-static.d.ts +16 -0
- package/dist/build/frontend-static.js +124 -0
- package/dist/build/server.d.ts +1 -0
- package/dist/build/server.js +6 -0
- package/dist/build/stacks.d.ts +1 -0
- package/dist/build/stacks.js +8 -0
- package/dist/build/views.d.ts +1 -0
- package/dist/build/views.js +11 -0
- package/dist/build.d.ts +9 -0
- package/dist/build.js +64 -0
- package/dist/bump.d.ts +1 -0
- package/dist/bump.js +163 -0
- package/dist/cdn/invalidate-cache.d.ts +1 -0
- package/dist/cdn/invalidate-cache.js +0 -0
- package/dist/changelog.d.ts +9 -0
- package/dist/changelog.js +27 -0
- package/dist/check/ports.d.ts +1 -0
- package/dist/check/ports.js +16 -0
- package/dist/clean.d.ts +1 -0
- package/dist/clean.js +4 -0
- package/dist/commit.d.ts +3 -0
- package/dist/commit.js +12 -0
- package/dist/copy-types.d.ts +1 -0
- package/dist/copy-types.js +10 -0
- package/dist/database/seed.d.ts +1 -0
- package/dist/database/seed.js +26 -0
- package/dist/deploy/create-receipt-rule.d.ts +1 -0
- package/dist/deploy/create-receipt-rule.js +36 -0
- package/dist/deploy/index.d.ts +1 -0
- package/dist/deploy/index.js +1574 -0
- package/dist/dev/api.d.ts +1 -0
- package/dist/dev/api.js +94 -0
- package/dist/dev/components.d.ts +1 -0
- package/dist/dev/components.js +7 -0
- package/dist/dev/dashboard-globals.d.ts +4 -0
- package/dist/dev/dashboard-globals.js +89 -0
- package/dist/dev/dashboard-utils.d.ts +150 -0
- package/dist/dev/dashboard-utils.js +380 -0
- package/dist/dev/dashboard.d.ts +22 -0
- package/dist/dev/dashboard.js +435 -0
- package/dist/dev/desktop.d.ts +1 -0
- package/dist/dev/desktop.js +5 -0
- package/dist/dev/docs.d.ts +1 -0
- package/dist/dev/docs.js +24 -0
- package/dist/dev/index.d.ts +10 -0
- package/dist/dev/index.js +29 -0
- package/dist/dev/migrate-watcher.d.ts +1 -0
- package/dist/dev/migrate-watcher.js +74 -0
- package/dist/dev/system-tray.d.ts +1 -0
- package/dist/dev/system-tray.js +5 -0
- package/dist/dev/views.d.ts +1 -0
- package/dist/dev/views.js +147 -0
- package/dist/discover-packages.d.ts +18 -0
- package/dist/discover-packages.js +32 -0
- package/dist/domains/add.d.ts +1 -0
- package/dist/domains/add.js +61 -0
- package/dist/domains/purchase.d.ts +1 -0
- package/dist/domains/purchase.js +66 -0
- package/dist/domains/remove.d.ts +1 -0
- package/dist/domains/remove.js +24 -0
- package/dist/examples.d.ts +5 -0
- package/dist/examples.js +34 -0
- package/dist/find-projects.d.ts +1 -0
- package/dist/find-projects.js +16 -0
- package/dist/fresh.d.ts +1 -0
- package/dist/fresh.js +17 -0
- package/dist/generate/action-types.d.ts +1 -0
- package/dist/generate/action-types.js +13 -0
- package/dist/generate/component-meta.d.ts +1 -0
- package/dist/generate/component-meta.js +9 -0
- package/dist/generate/custom-cli-file.d.ts +1 -0
- package/dist/generate/custom-cli-file.js +21 -0
- package/dist/generate/env-files.d.ts +1 -0
- package/dist/generate/env-files.js +66 -0
- package/dist/generate/ide-helpers.d.ts +1 -0
- package/dist/generate/ide-helpers.js +8 -0
- package/dist/generate/index.d.ts +36 -0
- package/dist/generate/index.js +158 -0
- package/dist/generate/lib-entries.d.ts +1 -0
- package/dist/generate/lib-entries.js +16 -0
- package/dist/generate/pantry-file.d.ts +1 -0
- package/dist/generate/pantry-file.js +46 -0
- package/dist/generate/symlink-core.d.ts +1 -0
- package/dist/generate/symlink-core.js +2 -0
- package/dist/generate/types.d.ts +1 -0
- package/dist/generate/types.js +3 -0
- package/dist/generate/vscode-custom-data.d.ts +1 -0
- package/dist/generate/vscode-custom-data.js +9 -0
- package/dist/generate/web-types.d.ts +1 -0
- package/dist/generate/web-types.js +0 -0
- package/dist/helpers/component-meta.d.ts +26 -0
- package/dist/helpers/component-meta.js +295 -0
- package/dist/helpers/index.d.ts +4 -0
- package/dist/helpers/index.js +1 -0
- package/dist/helpers/lib-entries.d.ts +12 -0
- package/dist/helpers/lib-entries.js +73 -0
- package/dist/helpers/package-json.d.ts +2 -0
- package/dist/helpers/package-json.js +74 -0
- package/dist/helpers/utils.d.ts +23 -0
- package/dist/helpers/utils.js +105 -0
- package/dist/helpers/vscode-custom-data.d.ts +3 -0
- package/dist/helpers/vscode-custom-data.js +30 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +50 -0
- package/dist/key-generate.d.ts +1 -0
- package/dist/key-generate.js +24 -0
- package/dist/lint/fix.d.ts +1 -0
- package/dist/lint/fix.js +4 -0
- package/dist/lint/index.d.ts +1 -0
- package/dist/lint/index.js +4 -0
- package/dist/lint/lint.d.ts +14 -0
- package/dist/lint/lint.js +34 -0
- package/dist/make-command.d.ts +10 -0
- package/dist/make-command.js +95 -0
- package/dist/make-job.d.ts +5 -0
- package/dist/make-job.js +154 -0
- package/dist/make-policy.d.ts +10 -0
- package/dist/make-policy.js +142 -0
- package/dist/make-resource.d.ts +10 -0
- package/dist/make-resource.js +142 -0
- package/dist/make.d.ts +52 -0
- package/dist/make.js +328 -0
- package/dist/migrate/database.d.ts +1 -0
- package/dist/migrate/database.js +16 -0
- package/dist/migrate/fresh.d.ts +1 -0
- package/dist/migrate/fresh.js +31 -0
- package/dist/orm/base.d.ts +1 -0
- package/dist/orm/base.js +0 -0
- package/dist/orm/test.d.ts +1 -0
- package/dist/orm/test.js +0 -0
- package/dist/prepublish.d.ts +1 -0
- package/dist/prepublish.js +16 -0
- package/dist/queue/clear.d.ts +1 -0
- package/dist/queue/clear.js +42 -0
- package/dist/queue/dlq-purge.d.ts +1 -0
- package/dist/queue/dlq-purge.js +28 -0
- package/dist/queue/dlq-retry.d.ts +1 -0
- package/dist/queue/dlq-retry.js +35 -0
- package/dist/queue/dlq.d.ts +1 -0
- package/dist/queue/dlq.js +55 -0
- package/dist/queue/failed.d.ts +1 -0
- package/dist/queue/failed.js +69 -0
- package/dist/queue/flush.d.ts +1 -0
- package/dist/queue/flush.js +49 -0
- package/dist/queue/inspect.d.ts +1 -0
- package/dist/queue/inspect.js +154 -0
- package/dist/queue/list.d.ts +1 -0
- package/dist/queue/list.js +69 -0
- package/dist/queue/monitor.d.ts +1 -0
- package/dist/queue/monitor.js +95 -0
- package/dist/queue/pause.d.ts +1 -0
- package/dist/queue/pause.js +28 -0
- package/dist/queue/quarantine.d.ts +1 -0
- package/dist/queue/quarantine.js +42 -0
- package/dist/queue/resume.d.ts +1 -0
- package/dist/queue/resume.js +27 -0
- package/dist/queue/retry.d.ts +1 -0
- package/dist/queue/retry.js +41 -0
- package/dist/queue/schedule-list.d.ts +1 -0
- package/dist/queue/schedule-list.js +33 -0
- package/dist/queue/schedule.d.ts +1 -0
- package/dist/queue/schedule.js +12 -0
- package/dist/queue/status.d.ts +1 -0
- package/dist/queue/status.js +68 -0
- package/dist/queue/table.d.ts +1 -0
- package/dist/queue/table.js +10 -0
- package/dist/queue/unquarantine.d.ts +1 -0
- package/dist/queue/unquarantine.js +27 -0
- package/dist/queue/work.d.ts +1 -0
- package/dist/queue/work.js +57 -0
- package/dist/release.d.ts +1 -0
- package/dist/release.js +21 -0
- package/dist/route/list.d.ts +1 -0
- package/dist/route/list.js +78 -0
- package/dist/run-action.d.ts +2 -0
- package/dist/run-action.js +1 -0
- package/dist/saas/setup.d.ts +1 -0
- package/dist/saas/setup.js +9 -0
- package/dist/scaffold-crud.d.ts +25 -0
- package/dist/scaffold-crud.js +211 -0
- package/dist/schedule/run.d.ts +1 -0
- package/dist/schedule/run.js +7 -0
- package/dist/search/flush.d.ts +1 -0
- package/dist/search/flush.js +10 -0
- package/dist/search/import.d.ts +1 -0
- package/dist/search/import.js +10 -0
- package/dist/search/settings-list.d.ts +1 -0
- package/dist/search/settings-list.js +10 -0
- package/dist/search/settings.d.ts +1 -0
- package/dist/search/settings.js +9 -0
- package/dist/serve/api.d.ts +1 -0
- package/dist/serve/api.js +35 -0
- package/dist/setup/index.d.ts +1 -0
- package/dist/setup/index.js +1 -0
- package/dist/setup/ssl.d.ts +38 -0
- package/dist/setup/ssl.js +271 -0
- package/dist/stacks.d.ts +4 -0
- package/dist/stacks.js +265 -0
- package/dist/templates.d.ts +28 -0
- package/dist/templates.js +301 -0
- package/dist/test/feature.d.ts +1 -0
- package/dist/test/feature.js +11 -0
- package/dist/test/index.d.ts +1 -0
- package/dist/test/index.js +11 -0
- package/dist/test/ui.d.ts +1 -0
- package/dist/test/ui.js +4 -0
- package/dist/test/unit.d.ts +1 -0
- package/dist/test/unit.js +11 -0
- package/dist/typecheck.d.ts +1 -0
- package/dist/typecheck.js +4 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.js +16 -0
- package/dist/upgrade/binary.d.ts +1 -0
- package/dist/upgrade/binary.js +29 -0
- package/dist/upgrade/bun.d.ts +1 -0
- package/dist/upgrade/bun.js +0 -0
- package/dist/upgrade/dependencies.d.ts +1 -0
- package/dist/upgrade/dependencies.js +0 -0
- package/dist/upgrade/framework-utils.d.ts +150 -0
- package/dist/upgrade/framework-utils.js +193 -0
- package/dist/upgrade/framework.d.ts +1 -0
- package/dist/upgrade/framework.js +310 -0
- package/dist/upgrade/index.d.ts +1 -0
- package/dist/upgrade/index.js +17 -0
- package/dist/upgrade/packages.d.ts +14 -0
- package/dist/upgrade/packages.js +99 -0
- package/dist/upgrade/shell.d.ts +1 -0
- package/dist/upgrade/shell.js +41 -0
- package/dist/upgrade.d.ts +7 -0
- package/dist/upgrade.js +38 -0
- package/package.json +16 -16
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { FailedJob } from "@stacksjs/orm";
|
|
4
|
+
const options = parseArgs(), queueName = options.queue, force = options.force === "true";
|
|
5
|
+
try {
|
|
6
|
+
if (!force) {
|
|
7
|
+
log.warn("This will permanently delete all failed jobs. Use --force to confirm.");
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
let deletedCount = 0;
|
|
11
|
+
if (queueName) {
|
|
12
|
+
log.info(`Flushing all failed jobs from queue "${queueName}"...`);
|
|
13
|
+
const failedJobs = await FailedJob.where("queue", queueName).get();
|
|
14
|
+
deletedCount = failedJobs.length;
|
|
15
|
+
for (const job of failedJobs)
|
|
16
|
+
await job.delete();
|
|
17
|
+
} else {
|
|
18
|
+
log.info("Flushing all failed jobs from all queues...");
|
|
19
|
+
const failedJobs = await FailedJob.all();
|
|
20
|
+
deletedCount = failedJobs.length;
|
|
21
|
+
for (const job of failedJobs)
|
|
22
|
+
await job.delete();
|
|
23
|
+
}
|
|
24
|
+
log.success(`Flushed ${deletedCount} failed job(s)`);
|
|
25
|
+
process.exit(0);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
28
|
+
if (/no such table:\s*(?:jobs|failed_jobs)\b/i.test(message)) {
|
|
29
|
+
process.stdout.write(`Queue tables are not set up yet.
|
|
30
|
+
`);
|
|
31
|
+
process.stdout.write("Run `buddy queue:table` to create the migrations, then `buddy migrate` to apply them.\n");
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
log.error("Failed to flush failed jobs", error);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
function parseArgs() {
|
|
38
|
+
const args = {};
|
|
39
|
+
process.argv.forEach((arg) => {
|
|
40
|
+
if (arg.startsWith("--")) {
|
|
41
|
+
const [key, value] = arg.slice(2).split("=");
|
|
42
|
+
if (key && value)
|
|
43
|
+
args[key] = value;
|
|
44
|
+
else if (key)
|
|
45
|
+
args[key] = "true";
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return args;
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { Job, FailedJob } from "@stacksjs/orm";
|
|
4
|
+
const options = parseArgs(), jobId = options.id, failedFlag = options.failed === "true";
|
|
5
|
+
try {
|
|
6
|
+
if (!jobId) {
|
|
7
|
+
log.error("Please provide a job ID");
|
|
8
|
+
log.info("Usage: buddy queue:inspect --id=<jobId> [--failed]");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const id = Number.parseInt(jobId, 10);
|
|
12
|
+
if (Number.isNaN(id)) {
|
|
13
|
+
log.error("Invalid job ID provided");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
if (failedFlag) {
|
|
17
|
+
const failedResult = await FailedJob.find(id);
|
|
18
|
+
if (!failedResult) {
|
|
19
|
+
log.error(`Failed job with ID ${id} not found`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const job = failedResult;
|
|
23
|
+
console.log(`
|
|
24
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`);
|
|
25
|
+
console.log("\u2502 Failed Job Details \u2502");
|
|
26
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
27
|
+
console.log(`\u2502 ID: ${String(job.id).padEnd(52)} \u2502`);
|
|
28
|
+
console.log(`\u2502 Queue: ${(job.queue || "default").padEnd(52)} \u2502`);
|
|
29
|
+
console.log(`\u2502 Connection: ${(job.connection || "database").padEnd(52)} \u2502`);
|
|
30
|
+
console.log(`\u2502 Failed At: ${(job.failed_at || "N/A").padEnd(52)} \u2502`);
|
|
31
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
32
|
+
console.log("\u2502 Exception: \u2502");
|
|
33
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
34
|
+
const exception = job.exception || "Unknown error", lines = exception.match(/.{1,62}/g) || [exception];
|
|
35
|
+
for (const line of lines.slice(0, 20))
|
|
36
|
+
console.log(`\u2502 ${line.padEnd(62)} \u2502`);
|
|
37
|
+
if (lines.length > 20)
|
|
38
|
+
console.log(`\u2502 ... (${lines.length - 20} more lines)`.padEnd(64) + " \u2502");
|
|
39
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
40
|
+
console.log("\u2502 Payload: \u2502");
|
|
41
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
42
|
+
if (job.payload)
|
|
43
|
+
try {
|
|
44
|
+
const payload = JSON.parse(job.payload);
|
|
45
|
+
console.log(`\u2502 Name: ${payload.name.padEnd(52)} \u2502`);
|
|
46
|
+
console.log(`\u2502 Path: ${payload.path.padEnd(52)} \u2502`);
|
|
47
|
+
console.log(`\u2502 Max Tries: ${String(payload.maxTries).padEnd(52)} \u2502`);
|
|
48
|
+
console.log("\u2502 Parameters: \u2502");
|
|
49
|
+
const paramLines = JSON.stringify(payload.params, null, 2).split(`
|
|
50
|
+
`);
|
|
51
|
+
for (const line of paramLines.slice(0, 15))
|
|
52
|
+
console.log(`\u2502 ${line.substring(0, 60).padEnd(60)} \u2502`);
|
|
53
|
+
if (paramLines.length > 15)
|
|
54
|
+
console.log(`\u2502 ... (${paramLines.length - 15} more lines)`.padEnd(64) + " \u2502");
|
|
55
|
+
} catch {
|
|
56
|
+
console.log(`\u2502 Raw: ${job.payload.substring(0, 58).padEnd(58)} \u2502`);
|
|
57
|
+
}
|
|
58
|
+
else
|
|
59
|
+
console.log("\u2502 (No payload) \u2502");
|
|
60
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
61
|
+
console.log(`
|
|
62
|
+
Actions:`);
|
|
63
|
+
console.log(` Retry this job: buddy queue:retry --id=${id}`);
|
|
64
|
+
console.log(` Delete this job: buddy queue:flush --id=${id}`);
|
|
65
|
+
} else {
|
|
66
|
+
const jobResult = await Job.find(id);
|
|
67
|
+
if (!jobResult) {
|
|
68
|
+
log.error(`Job with ID ${id} not found`);
|
|
69
|
+
log.info("Tip: Use --failed flag to inspect failed jobs");
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const job = jobResult, now = Math.floor(Date.now() / 1000);
|
|
73
|
+
let status = "Pending";
|
|
74
|
+
if (job.reserved_at)
|
|
75
|
+
status = "Processing";
|
|
76
|
+
else if (job.available_at && job.available_at > now)
|
|
77
|
+
status = "Delayed";
|
|
78
|
+
console.log(`
|
|
79
|
+
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`);
|
|
80
|
+
console.log("\u2502 Job Details \u2502");
|
|
81
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
82
|
+
console.log(`\u2502 ID: ${String(job.id).padEnd(50)} \u2502`);
|
|
83
|
+
console.log(`\u2502 Queue: ${(job.queue || "default").padEnd(50)} \u2502`);
|
|
84
|
+
console.log(`\u2502 Status: ${status.padEnd(50)} \u2502`);
|
|
85
|
+
console.log(`\u2502 Attempts: ${String(job.attempts || 0).padEnd(50)} \u2502`);
|
|
86
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
87
|
+
if (job.available_at) {
|
|
88
|
+
const availableDate = new Date(job.available_at * 1000);
|
|
89
|
+
console.log(`\u2502 Available At: ${availableDate.toISOString().padEnd(50)} \u2502`);
|
|
90
|
+
}
|
|
91
|
+
if (job.reserved_at) {
|
|
92
|
+
const reservedDate = new Date(job.reserved_at * 1000);
|
|
93
|
+
console.log(`\u2502 Reserved At: ${reservedDate.toISOString().padEnd(50)} \u2502`);
|
|
94
|
+
}
|
|
95
|
+
if (job.created_at)
|
|
96
|
+
console.log(`\u2502 Created At: ${String(job.created_at).padEnd(50)} \u2502`);
|
|
97
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
98
|
+
console.log("\u2502 Payload: \u2502");
|
|
99
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
100
|
+
if (job.payload)
|
|
101
|
+
try {
|
|
102
|
+
const payload = JSON.parse(job.payload);
|
|
103
|
+
console.log(`\u2502 Name: ${payload.name.padEnd(52)} \u2502`);
|
|
104
|
+
console.log(`\u2502 Path: ${payload.path.padEnd(52)} \u2502`);
|
|
105
|
+
console.log(`\u2502 Max Tries: ${String(payload.maxTries).padEnd(52)} \u2502`);
|
|
106
|
+
console.log(`\u2502 Timeout: ${String(payload.timeOut || "N/A").padEnd(52)} \u2502`);
|
|
107
|
+
console.log("\u2502 Parameters: \u2502");
|
|
108
|
+
const paramLines = JSON.stringify(payload.params, null, 2).split(`
|
|
109
|
+
`);
|
|
110
|
+
for (const line of paramLines.slice(0, 15))
|
|
111
|
+
console.log(`\u2502 ${line.substring(0, 60).padEnd(60)} \u2502`);
|
|
112
|
+
if (paramLines.length > 15)
|
|
113
|
+
console.log(`\u2502 ... (${paramLines.length - 15} more lines)`.padEnd(64) + " \u2502");
|
|
114
|
+
if (payload.classPayload)
|
|
115
|
+
try {
|
|
116
|
+
const classConfig = JSON.parse(payload.classPayload);
|
|
117
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
118
|
+
console.log("\u2502 Job Configuration: \u2502");
|
|
119
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
120
|
+
console.log(`\u2502 Tries: ${String(classConfig.tries || 3).padEnd(52)} \u2502`);
|
|
121
|
+
console.log(`\u2502 Timeout: ${String(classConfig.timeout || 60).padEnd(52)} \u2502`);
|
|
122
|
+
if (classConfig.backoff)
|
|
123
|
+
console.log(`\u2502 Backoff: ${String(JSON.stringify(classConfig.backoff)).substring(0, 52).padEnd(52)} \u2502`);
|
|
124
|
+
} catch {}
|
|
125
|
+
} catch {
|
|
126
|
+
console.log(`\u2502 Raw: ${job.payload.substring(0, 58).padEnd(58)} \u2502`);
|
|
127
|
+
}
|
|
128
|
+
else
|
|
129
|
+
console.log("\u2502 (No payload) \u2502");
|
|
130
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
131
|
+
if (status === "Delayed" && job.available_at) {
|
|
132
|
+
const waitTime = job.available_at - now;
|
|
133
|
+
console.log(`
|
|
134
|
+
This job will be available in ${waitTime} second(s)`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
process.exit(0);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
log.error("Failed to inspect job", error);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
function parseArgs() {
|
|
143
|
+
const args = {};
|
|
144
|
+
process.argv.forEach((arg) => {
|
|
145
|
+
if (arg.startsWith("--")) {
|
|
146
|
+
const [key, value] = arg.slice(2).split("=");
|
|
147
|
+
if (key && value)
|
|
148
|
+
args[key] = value;
|
|
149
|
+
else if (key)
|
|
150
|
+
args[key] = "true";
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
return args;
|
|
154
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { Job } from "@stacksjs/orm";
|
|
4
|
+
const options = parseArgs(), queueName = options.queue, statusFilter = options.status, limit = options.limit ? Number.parseInt(options.limit, 10) : 50;
|
|
5
|
+
try {
|
|
6
|
+
log.info(`Fetching queued jobs...
|
|
7
|
+
`);
|
|
8
|
+
let jobs = queueName ? await Job.where("queue", queueName).get() : await Job.all();
|
|
9
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
10
|
+
if (statusFilter === "pending")
|
|
11
|
+
jobs = jobs.filter((j) => !j.reserved_at && (j.available_at ?? 0) <= nowSec);
|
|
12
|
+
else if (statusFilter === "reserved")
|
|
13
|
+
jobs = jobs.filter((j) => Boolean(j.reserved_at));
|
|
14
|
+
else if (statusFilter === "delayed")
|
|
15
|
+
jobs = jobs.filter((j) => !j.reserved_at && (j.available_at ?? 0) > nowSec);
|
|
16
|
+
if (Number.isFinite(limit) && limit > 0)
|
|
17
|
+
jobs = jobs.slice(0, limit);
|
|
18
|
+
if (jobs.length === 0) {
|
|
19
|
+
console.log("\u2713 No queued jobs found");
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
23
|
+
console.log("\u2502 ID \u2502 Queue \u2502 Job \u2502 Attempts \u2502 Status \u2502 Available At \u2502");
|
|
24
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
25
|
+
for (const j of jobs) {
|
|
26
|
+
const jobAny = j, id = String(jobAny.id ?? "").padEnd(3), queue = String(jobAny.queue ?? "default").substring(0, 18).padEnd(18), jobName = extractJobName(jobAny.payload).substring(0, 32).padEnd(32), attempts = String(jobAny.attempts ?? 0).padEnd(8), status = (jobAny.reserved_at ? "reserved" : (jobAny.available_at ?? 0) > nowSec ? "delayed" : "pending").padEnd(8), avail = formatUnixTime(jobAny.available_at).padEnd(19);
|
|
27
|
+
console.log(`\u2502 ${id} \u2502 ${queue} \u2502 ${jobName} \u2502 ${attempts} \u2502 ${status} \u2502 ${avail} \u2502`);
|
|
28
|
+
}
|
|
29
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
30
|
+
console.log(`
|
|
31
|
+
Showing ${jobs.length} job(s)${queueName ? ` on queue '${queueName}'` : ""}${statusFilter ? ` (status=${statusFilter})` : ""}`);
|
|
32
|
+
process.exit(0);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
35
|
+
if (/no such table:\s*jobs\b/i.test(message)) {
|
|
36
|
+
process.stdout.write(`Queue tables are not set up yet.
|
|
37
|
+
`);
|
|
38
|
+
process.stdout.write("Run `buddy queue:table` to create the migrations, then `buddy migrate` to apply them.\n");
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
log.error("Failed to list queued jobs", error);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
function extractJobName(payload) {
|
|
45
|
+
if (typeof payload !== "string")
|
|
46
|
+
return "unknown";
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(payload);
|
|
49
|
+
return parsed.jobName ?? parsed.name ?? "unknown";
|
|
50
|
+
} catch {
|
|
51
|
+
return "unknown";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function formatUnixTime(seconds) {
|
|
55
|
+
if (typeof seconds !== "number" || !Number.isFinite(seconds))
|
|
56
|
+
return "N/A";
|
|
57
|
+
return new Date(seconds * 1000).toISOString().slice(0, 19).replace("T", " ");
|
|
58
|
+
}
|
|
59
|
+
function parseArgs() {
|
|
60
|
+
const args = {};
|
|
61
|
+
process.argv.forEach((arg) => {
|
|
62
|
+
if (arg.startsWith("--")) {
|
|
63
|
+
const [key, value] = arg.slice(2).split("=");
|
|
64
|
+
if (key && value)
|
|
65
|
+
args[key] = value;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return args;
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { Job, FailedJob } from "@stacksjs/orm";
|
|
4
|
+
import { getActiveJobCount, isWorkerRunning } from "@stacksjs/queue";
|
|
5
|
+
const options = parseArgs(), queueName = options.queue, refreshInterval = Number(options.interval) || 2000;
|
|
6
|
+
let isRunning = !0;
|
|
7
|
+
process.on("SIGINT", () => {
|
|
8
|
+
isRunning = !1;
|
|
9
|
+
console.log(`
|
|
10
|
+
|
|
11
|
+
Monitoring stopped.`);
|
|
12
|
+
process.exit(0);
|
|
13
|
+
});
|
|
14
|
+
process.on("SIGTERM", () => {
|
|
15
|
+
isRunning = !1;
|
|
16
|
+
process.exit(0);
|
|
17
|
+
});
|
|
18
|
+
async function getStats() {
|
|
19
|
+
const allJobs = queueName ? await Job.where("queue", queueName).get() : await Job.all(), failedJobs = queueName ? await FailedJob.where("queue", queueName).get() : await FailedJob.all(), now = Math.floor(Date.now() / 1000);
|
|
20
|
+
let pending = 0, processing = 0, delayed = 0;
|
|
21
|
+
for (const job of allJobs)
|
|
22
|
+
if (job.reserved_at)
|
|
23
|
+
processing++;
|
|
24
|
+
else if (job.available_at && Number(job.available_at) > now)
|
|
25
|
+
delayed++;
|
|
26
|
+
else
|
|
27
|
+
pending++;
|
|
28
|
+
return {
|
|
29
|
+
pending,
|
|
30
|
+
processing,
|
|
31
|
+
delayed,
|
|
32
|
+
failed: failedJobs.length,
|
|
33
|
+
total: allJobs.length,
|
|
34
|
+
workerRunning: isWorkerRunning(),
|
|
35
|
+
activeJobs: getActiveJobCount()
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async function displayStats() {
|
|
39
|
+
const stats = await getStats(), timestamp = new Date().toLocaleTimeString();
|
|
40
|
+
console.clear();
|
|
41
|
+
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
42
|
+
console.log("\u2502 Queue Monitor \u2502");
|
|
43
|
+
console.log("\u2502 Press Ctrl+C to exit \u2502");
|
|
44
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
45
|
+
console.log(`\u2502 Last Updated: ${timestamp.padEnd(48)} \u2502`);
|
|
46
|
+
console.log(`\u2502 Queue Filter: ${(queueName || "All Queues").padEnd(48)} \u2502`);
|
|
47
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
48
|
+
console.log("\u2502 \u2502");
|
|
49
|
+
console.log(`\u2502 Pending Jobs: ${String(stats.pending).padStart(8)} \u2502`);
|
|
50
|
+
console.log(`\u2502 Processing Jobs: ${String(stats.processing).padStart(8)} \u2502`);
|
|
51
|
+
console.log(`\u2502 Delayed Jobs: ${String(stats.delayed).padStart(8)} \u2502`);
|
|
52
|
+
console.log(`\u2502 Failed Jobs: ${String(stats.failed).padStart(8)} \u2502`);
|
|
53
|
+
console.log("\u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2502");
|
|
54
|
+
console.log(`\u2502 Total Jobs: ${String(stats.total).padStart(8)} \u2502`);
|
|
55
|
+
console.log("\u2502 \u2502");
|
|
56
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
57
|
+
console.log("\u2502 Worker Status \u2502");
|
|
58
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
59
|
+
console.log(`\u2502 Worker Running: ${(stats.workerRunning ? "Yes" : "No").padEnd(8)} \u2502`);
|
|
60
|
+
console.log(`\u2502 Active Jobs: ${String(stats.activeJobs).padStart(8)} \u2502`);
|
|
61
|
+
console.log("\u2502 \u2502");
|
|
62
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
63
|
+
if (stats.failed > 0)
|
|
64
|
+
console.log(`
|
|
65
|
+
\u26A0\uFE0F Warning: ${stats.failed} failed job(s) - run 'buddy queue:failed' for details`);
|
|
66
|
+
if (!stats.workerRunning && stats.pending > 0)
|
|
67
|
+
console.log(`
|
|
68
|
+
\u26A0\uFE0F Warning: Worker not running but ${stats.pending} job(s) pending - run 'buddy queue:work'`);
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
log.info(`Starting queue monitor...
|
|
72
|
+
`);
|
|
73
|
+
await displayStats();
|
|
74
|
+
while (isRunning) {
|
|
75
|
+
await new Promise((resolve) => setTimeout(resolve, refreshInterval));
|
|
76
|
+
if (isRunning)
|
|
77
|
+
await displayStats();
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
log.error("Monitor error:", error);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
function parseArgs() {
|
|
84
|
+
const args = {};
|
|
85
|
+
process.argv.forEach((arg) => {
|
|
86
|
+
if (arg.startsWith("--")) {
|
|
87
|
+
const [key, value] = arg.slice(2).split("=");
|
|
88
|
+
if (key && value)
|
|
89
|
+
args[key] = value;
|
|
90
|
+
else if (key)
|
|
91
|
+
args[key] = "true";
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
return args;
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { pauseQueue } from "@stacksjs/queue";
|
|
4
|
+
const options = parseArgs(), queue = options.queue;
|
|
5
|
+
if (!queue) {
|
|
6
|
+
log.error("Usage: buddy queue:pause --queue=<name> [--for=300]");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
const pauseSeconds = options.for ? Number.parseInt(options.for, 10) : 300;
|
|
10
|
+
try {
|
|
11
|
+
await pauseQueue(queue, pauseSeconds);
|
|
12
|
+
log.success(`Paused queue '${queue}' for ${pauseSeconds}s`);
|
|
13
|
+
process.exit(0);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
log.error("Failed to pause queue", error);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
function parseArgs() {
|
|
19
|
+
const args = {};
|
|
20
|
+
process.argv.forEach((arg) => {
|
|
21
|
+
if (arg.startsWith("--")) {
|
|
22
|
+
const [key, value] = arg.slice(2).split("=");
|
|
23
|
+
if (key && value)
|
|
24
|
+
args[key] = value;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return args;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { listQuarantined, quarantineJob } from "@stacksjs/queue";
|
|
4
|
+
const options = parseArgs();
|
|
5
|
+
try {
|
|
6
|
+
if (options.add) {
|
|
7
|
+
const jobName = options.add;
|
|
8
|
+
await quarantineJob(jobName);
|
|
9
|
+
log.success(`Quarantined ${jobName}`);
|
|
10
|
+
process.exit(0);
|
|
11
|
+
}
|
|
12
|
+
const rows = await listQuarantined();
|
|
13
|
+
if (rows.length === 0) {
|
|
14
|
+
console.log("\u2713 No jobs currently quarantined");
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
18
|
+
console.log("\u2502 ID \u2502 Job Name \u2502 Payload Hash \u2502 Failures \u2502 Quarantined At \u2502");
|
|
19
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
20
|
+
for (const row of rows) {
|
|
21
|
+
const id = String(row.id).padEnd(3), jn = (row.job_name || "?").substring(0, 32).padEnd(32), ph = (row.payload_hash || "?").substring(0, 16).padEnd(16), fc = String(row.failure_count ?? 0).padEnd(8), qa = (row.quarantined_at || "N/A").substring(0, 24).padEnd(24);
|
|
22
|
+
console.log(`\u2502 ${id} \u2502 ${jn} \u2502 ${ph} \u2502 ${fc} \u2502 ${qa} \u2502`);
|
|
23
|
+
}
|
|
24
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
25
|
+
console.log(`
|
|
26
|
+
${rows.length} quarantined entrie(s). Use \`queue:unquarantine <JobName>\` to lift.`);
|
|
27
|
+
process.exit(0);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
log.error("Failed to handle queue:quarantine", error);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
function parseArgs() {
|
|
33
|
+
const args = {};
|
|
34
|
+
process.argv.forEach((arg) => {
|
|
35
|
+
if (arg.startsWith("--")) {
|
|
36
|
+
const [key, value] = arg.slice(2).split("=");
|
|
37
|
+
if (key && value)
|
|
38
|
+
args[key] = value;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return args;
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { resumeQueue } from "@stacksjs/queue";
|
|
4
|
+
const options = parseArgs(), queue = options.queue;
|
|
5
|
+
if (!queue) {
|
|
6
|
+
log.error("Usage: buddy queue:resume --queue=<name>");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
await resumeQueue(queue);
|
|
11
|
+
log.success(`Resumed queue '${queue}'`);
|
|
12
|
+
process.exit(0);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
log.error("Failed to resume queue", error);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
function parseArgs() {
|
|
18
|
+
const args = {};
|
|
19
|
+
process.argv.forEach((arg) => {
|
|
20
|
+
if (arg.startsWith("--")) {
|
|
21
|
+
const [key, value] = arg.slice(2).split("=");
|
|
22
|
+
if (key && value)
|
|
23
|
+
args[key] = value;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return args;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { executeFailedJobs, retryFailedJob } from "@stacksjs/queue";
|
|
4
|
+
const options = parseArgs(), jobId = options.id, all = options.all === "true";
|
|
5
|
+
try {
|
|
6
|
+
if (all) {
|
|
7
|
+
log.info("Retrying all failed jobs...");
|
|
8
|
+
await executeFailedJobs();
|
|
9
|
+
log.success("All failed jobs have been queued for retry");
|
|
10
|
+
} else if (jobId) {
|
|
11
|
+
const id = Number.parseInt(jobId, 10);
|
|
12
|
+
if (Number.isNaN(id)) {
|
|
13
|
+
log.error("Invalid job ID provided");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
log.info(`Retrying failed job ${id}...`);
|
|
17
|
+
await retryFailedJob(id);
|
|
18
|
+
log.success(`Failed job ${id} has been queued for retry`);
|
|
19
|
+
} else {
|
|
20
|
+
log.warn("Please provide a job ID or use --all to retry all failed jobs");
|
|
21
|
+
log.info("Usage: buddy queue:retry <id> or buddy queue:retry --all");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
process.exit(0);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
log.error("Failed to retry job(s)", error);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
function parseArgs() {
|
|
30
|
+
const args = {};
|
|
31
|
+
process.argv.forEach((arg) => {
|
|
32
|
+
if (arg.startsWith("--")) {
|
|
33
|
+
const [key, value] = arg.slice(2).split("=");
|
|
34
|
+
if (key && value)
|
|
35
|
+
args[key] = value;
|
|
36
|
+
else if (key)
|
|
37
|
+
args[key] = "true";
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return args;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { discoverJobs, getScheduledJobs } from "@stacksjs/queue";
|
|
4
|
+
try {
|
|
5
|
+
log.info(`Discovering scheduled jobs...
|
|
6
|
+
`);
|
|
7
|
+
await discoverJobs();
|
|
8
|
+
const scheduledJobs = getScheduledJobs();
|
|
9
|
+
if (scheduledJobs.length === 0) {
|
|
10
|
+
log.info("No scheduled jobs found.");
|
|
11
|
+
log.info('Tip: Add a "rate" or "schedule" property to your job configuration.');
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
15
|
+
console.log("\u2502 Job Name \u2502 Schedule \u2502 Queue \u2502 Tries \u2502");
|
|
16
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
17
|
+
for (const job of scheduledJobs) {
|
|
18
|
+
const name = job.name.substring(0, 48).padEnd(48), schedule = (job.config.rate || job.config.schedule || "N/A").substring(0, 21).padEnd(21), queue = (job.config.queue || "default").substring(0, 9).padEnd(9), tries = String(job.config.tries || 3).padStart(7);
|
|
19
|
+
console.log(`\u2502 ${name} \u2502 ${schedule} \u2502 ${queue} \u2502${tries} \u2502`);
|
|
20
|
+
}
|
|
21
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
22
|
+
console.log(`
|
|
23
|
+
Total: ${scheduledJobs.length} scheduled job(s)`);
|
|
24
|
+
console.log(`
|
|
25
|
+
Schedule Formats:`);
|
|
26
|
+
console.log(' \u2022 Cron: "* * * * *" (minute hour day month dayOfWeek)');
|
|
27
|
+
console.log(" \u2022 Presets: @hourly, @daily, @weekly, @monthly");
|
|
28
|
+
console.log(" \u2022 Every: Every.Minute, Every.Hour, Every.Day");
|
|
29
|
+
process.exit(0);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
log.error("Failed to list scheduled jobs:", error);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { startScheduler } from "@stacksjs/queue";
|
|
4
|
+
log.info("Starting job scheduler...");
|
|
5
|
+
try {
|
|
6
|
+
await startScheduler();
|
|
7
|
+
log.success("Scheduler is running. Press Ctrl+C to stop.");
|
|
8
|
+
await new Promise(() => {});
|
|
9
|
+
} catch (error) {
|
|
10
|
+
log.error("Failed to start scheduler:", error);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|