workmatic 1.0.1 → 1.0.3
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/cli.cjs +87 -39
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +87 -39
- package/dist/cli.js.map +1 -1
- package/package.json +11 -2
package/dist/cli.cjs
CHANGED
|
@@ -27,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
28
28
|
var import_fs = require("fs");
|
|
29
29
|
var import_readline = require("readline");
|
|
30
|
+
var import_cli_table3 = __toESM(require("cli-table3"), 1);
|
|
30
31
|
var args = process.argv.slice(2);
|
|
31
32
|
var command = args[0];
|
|
32
33
|
function printUsage() {
|
|
@@ -131,30 +132,38 @@ async function cmdStats(dbPath) {
|
|
|
131
132
|
`).all();
|
|
132
133
|
const total = stats.reduce((sum, s) => sum + s.count, 0);
|
|
133
134
|
console.log("\n\u{1F4CA} Job Statistics\n");
|
|
134
|
-
console.log("By Status:");
|
|
135
|
-
console.log("\u2500".repeat(30));
|
|
136
135
|
const statusOrder = ["ready", "running", "done", "failed", "dead"];
|
|
137
136
|
const statusEmoji = {
|
|
138
137
|
ready: "\u23F3",
|
|
139
|
-
running: "\u25B6\uFE0F
|
|
138
|
+
running: "\u25B6\uFE0F",
|
|
140
139
|
done: "\u2705",
|
|
141
|
-
failed: "\u26A0\uFE0F
|
|
140
|
+
failed: "\u26A0\uFE0F",
|
|
142
141
|
dead: "\u{1F480}"
|
|
143
142
|
};
|
|
143
|
+
const statusTable = new import_cli_table3.default({
|
|
144
|
+
head: ["", "Status", "Count"],
|
|
145
|
+
style: { head: ["cyan"], border: ["gray"] },
|
|
146
|
+
colAligns: ["left", "left", "right"]
|
|
147
|
+
});
|
|
144
148
|
for (const status of statusOrder) {
|
|
145
149
|
const stat = stats.find((s) => s.status === status);
|
|
146
150
|
const count = stat?.count ?? 0;
|
|
147
|
-
const emoji = statusEmoji[status] || "
|
|
148
|
-
|
|
151
|
+
const emoji = statusEmoji[status] || "";
|
|
152
|
+
statusTable.push([emoji, status, count.toLocaleString()]);
|
|
149
153
|
}
|
|
150
|
-
|
|
151
|
-
console.log(
|
|
154
|
+
statusTable.push([{ colSpan: 2, content: "Total", hAlign: "right" }, total.toLocaleString()]);
|
|
155
|
+
console.log(statusTable.toString());
|
|
152
156
|
if (queues.length > 0) {
|
|
153
|
-
console.log("\
|
|
154
|
-
|
|
157
|
+
console.log("\n\u{1F4CB} By Queue\n");
|
|
158
|
+
const queueTable = new import_cli_table3.default({
|
|
159
|
+
head: ["Queue", "Jobs"],
|
|
160
|
+
style: { head: ["cyan"], border: ["gray"] },
|
|
161
|
+
colAligns: ["left", "right"]
|
|
162
|
+
});
|
|
155
163
|
for (const q of queues) {
|
|
156
|
-
|
|
164
|
+
queueTable.push([q.queue, q.count.toLocaleString()]);
|
|
157
165
|
}
|
|
166
|
+
console.log(queueTable.toString());
|
|
158
167
|
}
|
|
159
168
|
console.log();
|
|
160
169
|
} finally {
|
|
@@ -181,19 +190,35 @@ async function cmdList(dbPath, options) {
|
|
|
181
190
|
console.log("No jobs found.");
|
|
182
191
|
return;
|
|
183
192
|
}
|
|
184
|
-
|
|
185
|
-
|
|
193
|
+
const statusEmoji = {
|
|
194
|
+
ready: "\u23F3",
|
|
195
|
+
running: "\u25B6\uFE0F",
|
|
196
|
+
done: "\u2705",
|
|
197
|
+
failed: "\u26A0\uFE0F",
|
|
198
|
+
dead: "\u{1F480}"
|
|
199
|
+
};
|
|
200
|
+
const table = new import_cli_table3.default({
|
|
201
|
+
head: ["ID", "Queue", "Status", "Pri", "Attempts", "Created"],
|
|
202
|
+
style: { head: ["cyan"], border: ["gray"] },
|
|
203
|
+
colAligns: ["left", "left", "left", "right", "center", "left"],
|
|
204
|
+
colWidths: [24, 16, 12, 5, 10, 21],
|
|
205
|
+
wordWrap: true
|
|
206
|
+
});
|
|
186
207
|
for (const job of jobs) {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
208
|
+
const emoji = statusEmoji[job.status] || "";
|
|
209
|
+
table.push([
|
|
210
|
+
job.public_id.slice(0, 21),
|
|
211
|
+
job.queue.slice(0, 14),
|
|
212
|
+
`${emoji} ${job.status}`,
|
|
213
|
+
job.priority,
|
|
214
|
+
`${job.attempts}/${job.max_attempts}`,
|
|
215
|
+
new Date(job.created_at).toISOString().slice(0, 19)
|
|
216
|
+
]);
|
|
194
217
|
}
|
|
218
|
+
console.log();
|
|
219
|
+
console.log(table.toString());
|
|
195
220
|
console.log(`
|
|
196
|
-
Showing ${jobs.length} jobs
|
|
221
|
+
Showing ${jobs.length} jobs
|
|
197
222
|
`);
|
|
198
223
|
} finally {
|
|
199
224
|
db.close();
|
|
@@ -410,32 +435,55 @@ async function cmdResume(dbPath, queueName) {
|
|
|
410
435
|
async function cmdQueues(dbPath) {
|
|
411
436
|
const db = new import_better_sqlite3.default(dbPath, { readonly: true });
|
|
412
437
|
try {
|
|
413
|
-
const
|
|
414
|
-
SELECT
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
438
|
+
const settingsExists = db.prepare(`
|
|
439
|
+
SELECT name FROM sqlite_master WHERE type='table' AND name='workmatic_settings'
|
|
440
|
+
`).get();
|
|
441
|
+
let queues;
|
|
442
|
+
if (settingsExists) {
|
|
443
|
+
queues = db.prepare(`
|
|
444
|
+
SELECT
|
|
445
|
+
j.queue,
|
|
446
|
+
COUNT(*) as total,
|
|
447
|
+
SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,
|
|
448
|
+
SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,
|
|
449
|
+
COALESCE(s.paused, 0) as paused
|
|
450
|
+
FROM workmatic_jobs j
|
|
451
|
+
LEFT JOIN workmatic_settings s ON j.queue = s.queue
|
|
452
|
+
GROUP BY j.queue
|
|
453
|
+
`).all();
|
|
454
|
+
} else {
|
|
455
|
+
queues = db.prepare(`
|
|
456
|
+
SELECT
|
|
457
|
+
queue,
|
|
458
|
+
COUNT(*) as total,
|
|
459
|
+
SUM(CASE WHEN status = 'ready' THEN 1 ELSE 0 END) as ready,
|
|
460
|
+
SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running,
|
|
461
|
+
0 as paused
|
|
462
|
+
FROM workmatic_jobs
|
|
463
|
+
GROUP BY queue
|
|
464
|
+
`).all();
|
|
465
|
+
}
|
|
424
466
|
if (queues.length === 0) {
|
|
425
467
|
console.log("No queues found.");
|
|
426
468
|
return;
|
|
427
469
|
}
|
|
428
470
|
console.log("\n\u{1F4CB} Queues\n");
|
|
429
|
-
|
|
430
|
-
|
|
471
|
+
const table = new import_cli_table3.default({
|
|
472
|
+
head: ["Queue", "Status", "Ready", "Running", "Total"],
|
|
473
|
+
style: { head: ["cyan"], border: ["gray"] },
|
|
474
|
+
colAligns: ["left", "left", "right", "right", "right"]
|
|
475
|
+
});
|
|
431
476
|
for (const q of queues) {
|
|
432
|
-
const name = q.queue.padEnd(16);
|
|
433
477
|
const status = q.paused ? "\u23F8\uFE0F PAUSED" : "\u25B6\uFE0F ACTIVE";
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
478
|
+
table.push([
|
|
479
|
+
q.queue,
|
|
480
|
+
status,
|
|
481
|
+
q.ready.toLocaleString(),
|
|
482
|
+
q.running.toLocaleString(),
|
|
483
|
+
q.total.toLocaleString()
|
|
484
|
+
]);
|
|
438
485
|
}
|
|
486
|
+
console.log(table.toString());
|
|
439
487
|
console.log();
|
|
440
488
|
} finally {
|
|
441
489
|
db.close();
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Workmatic CLI\n * \n * Commands:\n * stats <db> Show job statistics\n * export <db> [output] Export jobs to CSV\n * import <db> <input> Import jobs from CSV\n * purge <db> [--status=done] Delete jobs by status\n * retry <db> [--status=dead] Retry failed/dead jobs\n * pause <db> <queue> Pause a queue (workers stop claiming)\n * resume <db> <queue> Resume a paused queue\n */\n\nimport Database from 'better-sqlite3';\nimport { createReadStream, createWriteStream } from 'fs';\nimport { createInterface } from 'readline';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printUsage(): void {\n console.log(`\nWorkmatic CLI - Job Queue Management\n\nUsage:\n workmatic <command> [options]\n\nCommands:\n stats <db> Show job statistics\n queues <db> List all queues with status\n list <db> [--status=<status>] List jobs (optionally filter by status)\n export <db> [output.csv] Export jobs to CSV (default: stdout)\n import <db> <input.csv> Import jobs from CSV\n purge <db> --status=<status> Delete jobs by status\n retry <db> [--status=dead] Retry dead/failed jobs (reset to ready)\n pause <db> <queue> Pause a queue (running workers stop claiming)\n resume <db> <queue> Resume a paused queue\n\nOptions:\n --status=<status> Filter by status (ready|running|done|failed|dead)\n --queue=<queue> Filter by queue name\n --limit=<n> Limit number of results (default: 100)\n\nExamples:\n workmatic stats ./jobs.db\n workmatic list ./jobs.db --status=dead --limit=10\n workmatic export ./jobs.db backup.csv\n workmatic export ./jobs.db --status=failed > failed-jobs.csv\n workmatic import ./jobs.db backup.csv\n workmatic purge ./jobs.db --status=done\n workmatic retry ./jobs.db --status=dead\n workmatic pause ./jobs.db emails\n workmatic resume ./jobs.db emails\n`);\n}\n\nfunction parseOptions(args: string[]): Record<string, string> {\n const options: Record<string, string> = {};\n for (const arg of args) {\n if (arg.startsWith('--')) {\n const [key, value] = arg.slice(2).split('=');\n options[key] = value ?? 'true';\n }\n }\n return options;\n}\n\nfunction getPositionalArgs(args: string[]): string[] {\n return args.filter(arg => !arg.startsWith('--'));\n}\n\n// CSV helpers\nfunction escapeCSV(value: string | null): string {\n if (value === null) return '';\n const str = String(value);\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\nfunction parseCSVLine(line: string): string[] {\n const result: string[] = [];\n let current = '';\n let inQuotes = false;\n \n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n \n if (inQuotes) {\n if (char === '\"') {\n if (line[i + 1] === '\"') {\n current += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n current += char;\n }\n } else {\n if (char === '\"') {\n inQuotes = true;\n } else if (char === ',') {\n result.push(current);\n current = '';\n } else {\n current += char;\n }\n }\n }\n \n result.push(current);\n return result;\n}\n\n// Commands\nasync function cmdStats(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n const stats = db.prepare(`\n SELECT status, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY status\n `).all() as { status: string; count: number }[];\n \n const queues = db.prepare(`\n SELECT queue, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY queue\n `).all() as { queue: string; count: number }[];\n \n const total = stats.reduce((sum, s) => sum + s.count, 0);\n \n console.log('\\n📊 Job Statistics\\n');\n console.log('By Status:');\n console.log('─'.repeat(30));\n \n const statusOrder = ['ready', 'running', 'done', 'failed', 'dead'];\n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▶️ ',\n done: '✅',\n failed: '⚠️ ',\n dead: '💀',\n };\n \n for (const status of statusOrder) {\n const stat = stats.find(s => s.status === status);\n const count = stat?.count ?? 0;\n const emoji = statusEmoji[status] || ' ';\n console.log(` ${emoji} ${status.padEnd(10)} ${count.toLocaleString().padStart(8)}`);\n }\n console.log('─'.repeat(30));\n console.log(` Total: ${total.toLocaleString().padStart(8)}`);\n \n if (queues.length > 0) {\n console.log('\\nBy Queue:');\n console.log('─'.repeat(30));\n for (const q of queues) {\n console.log(` ${q.queue.padEnd(15)} ${q.count.toLocaleString().padStart(8)}`);\n }\n }\n \n console.log();\n } finally {\n db.close();\n }\n}\n\nasync function cmdList(dbPath: string, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT public_id, queue, status, priority, attempts, max_attempts, created_at, last_error FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY created_at DESC LIMIT ?';\n params.push(parseInt(options.limit || '100', 10));\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n if (jobs.length === 0) {\n console.log('No jobs found.');\n return;\n }\n \n console.log('\\nID Queue Status Pri Attempts Created');\n console.log('─'.repeat(85));\n \n for (const job of jobs) {\n const id = job.public_id.slice(0, 21);\n const queue = job.queue.slice(0, 15).padEnd(15);\n const status = job.status.padEnd(9);\n const pri = String(job.priority).padStart(3);\n const attempts = `${job.attempts}/${job.max_attempts}`.padStart(8);\n const created = new Date(job.created_at).toISOString().slice(0, 19);\n \n console.log(`${id} ${queue} ${status} ${pri} ${attempts} ${created}`);\n }\n \n console.log(`\\nShowing ${jobs.length} jobs\\n`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdExport(dbPath: string, outputPath: string | undefined, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT * FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY id';\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n const output = outputPath ? createWriteStream(outputPath) : process.stdout;\n \n // Header\n const columns = [\n 'public_id', 'queue', 'payload', 'status', 'priority',\n 'run_at', 'attempts', 'max_attempts', 'lease_until',\n 'created_at', 'updated_at', 'last_error'\n ];\n \n if (outputPath) {\n output.write(columns.join(',') + '\\n');\n } else {\n console.log(columns.join(','));\n }\n \n // Rows\n for (const job of jobs) {\n const row = columns.map(col => escapeCSV(job[col]));\n if (outputPath) {\n output.write(row.join(',') + '\\n');\n } else {\n console.log(row.join(','));\n }\n }\n \n if (outputPath) {\n (output as any).end();\n console.error(`✅ Exported ${jobs.length} jobs to ${outputPath}`);\n }\n } finally {\n db.close();\n }\n}\n\nasync function cmdImport(dbPath: string, inputPath: string): Promise<void> {\n const db = new Database(dbPath);\n \n // Ensure schema exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n public_id TEXT UNIQUE NOT NULL,\n queue TEXT NOT NULL,\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'ready',\n priority INTEGER NOT NULL DEFAULT 0,\n run_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n lease_until INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n last_error TEXT\n )\n `);\n \n const insert = db.prepare(`\n INSERT OR REPLACE INTO workmatic_jobs \n (public_id, queue, payload, status, priority, run_at, attempts, max_attempts, lease_until, created_at, updated_at, last_error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const rl = createInterface({\n input: createReadStream(inputPath),\n crlfDelay: Infinity,\n });\n \n let lineNum = 0;\n let imported = 0;\n let columns: string[] = [];\n \n try {\n const insertMany = db.transaction((rows: any[]) => {\n for (const row of rows) {\n insert.run(\n row.public_id,\n row.queue,\n row.payload,\n row.status,\n parseInt(row.priority, 10) || 0,\n parseInt(row.run_at, 10) || Date.now(),\n parseInt(row.attempts, 10) || 0,\n parseInt(row.max_attempts, 10) || 3,\n parseInt(row.lease_until, 10) || 0,\n parseInt(row.created_at, 10) || Date.now(),\n parseInt(row.updated_at, 10) || Date.now(),\n row.last_error || null\n );\n imported++;\n }\n });\n \n const batch: any[] = [];\n \n for await (const line of rl) {\n lineNum++;\n \n if (lineNum === 1) {\n // Header row\n columns = parseCSVLine(line);\n continue;\n }\n \n if (!line.trim()) continue;\n \n const values = parseCSVLine(line);\n const row: Record<string, string> = {};\n \n for (let i = 0; i < columns.length; i++) {\n row[columns[i]] = values[i] ?? '';\n }\n \n batch.push(row);\n \n // Batch insert every 1000 rows\n if (batch.length >= 1000) {\n insertMany(batch);\n batch.length = 0;\n process.stderr.write(`\\rImported ${imported} jobs...`);\n }\n }\n \n // Insert remaining\n if (batch.length > 0) {\n insertMany(batch);\n }\n \n console.log(`\\n✅ Imported ${imported} jobs from ${inputPath}`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPurge(dbPath: string, options: Record<string, string>): Promise<void> {\n if (!options.status) {\n console.error('Error: --status is required for purge command');\n console.error('Example: workmatic purge ./jobs.db --status=done');\n process.exit(1);\n }\n \n const db = new Database(dbPath);\n \n try {\n const result = db.prepare('DELETE FROM workmatic_jobs WHERE status = ?').run(options.status);\n console.log(`🗑️ Deleted ${result.changes} jobs with status '${options.status}'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdRetry(dbPath: string, options: Record<string, string>): Promise<void> {\n const status = options.status || 'dead';\n const db = new Database(dbPath);\n \n try {\n const now = Date.now();\n const result = db.prepare(`\n UPDATE workmatic_jobs \n SET status = 'ready', \n attempts = 0, \n run_at = ?, \n lease_until = 0,\n updated_at = ?\n WHERE status = ?\n `).run(now, now, status);\n \n console.log(`🔄 Reset ${result.changes} jobs from '${status}' to 'ready'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPause(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 1, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 1, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`⏸️ Paused queue '${queueName}'`);\n console.log(` Running workers will stop claiming new jobs.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdResume(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 0, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 0, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`▶️ Resumed queue '${queueName}'`);\n console.log(` Workers will start claiming jobs again.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdQueues(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n // Get all queues with their job counts and pause state\n const queues = db.prepare(`\n SELECT \n j.queue,\n COUNT(*) as total,\n SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,\n COALESCE(s.paused, 0) as paused\n FROM workmatic_jobs j\n LEFT JOIN workmatic_settings s ON j.queue = s.queue\n GROUP BY j.queue\n `).all() as { queue: string; total: number; ready: number; running: number; paused: number }[];\n \n if (queues.length === 0) {\n console.log('No queues found.');\n return;\n }\n \n console.log('\\n📋 Queues\\n');\n console.log('Queue Status Ready Running Total');\n console.log('─'.repeat(55));\n \n for (const q of queues) {\n const name = q.queue.padEnd(16);\n const status = q.paused ? '⏸️ PAUSED' : '▶️ ACTIVE';\n const ready = String(q.ready).padStart(5);\n const running = String(q.running).padStart(8);\n const total = String(q.total).padStart(6);\n \n console.log(`${name} ${status} ${ready} ${running} ${total}`);\n }\n \n console.log();\n } finally {\n db.close();\n }\n}\n\n// Main\nasync function main(): Promise<void> {\n if (!command || command === '--help' || command === '-h') {\n printUsage();\n process.exit(0);\n }\n \n const positionalArgs = getPositionalArgs(args.slice(1));\n const options = parseOptions(args);\n \n const dbPath = positionalArgs[0];\n \n if (!dbPath) {\n console.error('Error: Database path is required');\n printUsage();\n process.exit(1);\n }\n \n try {\n switch (command) {\n case 'stats':\n await cmdStats(dbPath);\n break;\n \n case 'list':\n await cmdList(dbPath, options);\n break;\n \n case 'export':\n await cmdExport(dbPath, positionalArgs[1], options);\n break;\n \n case 'import':\n if (!positionalArgs[1]) {\n console.error('Error: Input CSV file is required');\n process.exit(1);\n }\n await cmdImport(dbPath, positionalArgs[1]);\n break;\n \n case 'purge':\n await cmdPurge(dbPath, options);\n break;\n \n case 'retry':\n await cmdRetry(dbPath, options);\n break;\n \n case 'pause':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic pause ./jobs.db emails');\n process.exit(1);\n }\n await cmdPause(dbPath, positionalArgs[1]);\n break;\n \n case 'resume':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic resume ./jobs.db emails');\n process.exit(1);\n }\n await cmdResume(dbPath, positionalArgs[1]);\n break;\n \n case 'queues':\n await cmdQueues(dbPath);\n break;\n \n default:\n console.error(`Unknown command: ${command}`);\n printUsage();\n process.exit(1);\n }\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,4BAAqB;AACrB,gBAAoD;AACpD,sBAAgC;AAGhC,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UAAU,KAAK,CAAC;AAEtB,SAAS,aAAmB;AAC1B,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAgCb;AACD;AAEA,SAAS,aAAaA,OAAwC;AAC5D,QAAM,UAAkC,CAAC;AACzC,aAAW,OAAOA,OAAM;AACtB,QAAI,IAAI,WAAW,IAAI,GAAG;AACxB,YAAM,CAAC,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,EAAE,MAAM,GAAG;AAC3C,cAAQ,GAAG,IAAI,SAAS;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,kBAAkBA,OAA0B;AACnD,SAAOA,MAAK,OAAO,SAAO,CAAC,IAAI,WAAW,IAAI,CAAC;AACjD;AAGA,SAAS,UAAU,OAA8B;AAC/C,MAAI,UAAU,KAAM,QAAO;AAC3B,QAAM,MAAM,OAAO,KAAK;AACxB,MAAI,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,IAAI,GAAG;AAChE,WAAO,IAAI,IAAI,QAAQ,MAAM,IAAI,CAAC;AAAA,EACpC;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAwB;AAC5C,QAAM,SAAmB,CAAC;AAC1B,MAAI,UAAU;AACd,MAAI,WAAW;AAEf,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,OAAO,KAAK,CAAC;AAEnB,QAAI,UAAU;AACZ,UAAI,SAAS,KAAK;AAChB,YAAI,KAAK,IAAI,CAAC,MAAM,KAAK;AACvB,qBAAW;AACX;AAAA,QACF,OAAO;AACL,qBAAW;AAAA,QACb;AAAA,MACF,OAAO;AACL,mBAAW;AAAA,MACb;AAAA,IACF,OAAO;AACL,UAAI,SAAS,KAAK;AAChB,mBAAW;AAAA,MACb,WAAW,SAAS,KAAK;AACvB,eAAO,KAAK,OAAO;AACnB,kBAAU;AAAA,MACZ,OAAO;AACL,mBAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAEA,SAAO,KAAK,OAAO;AACnB,SAAO;AACT;AAGA,eAAe,SAAS,QAA+B;AACrD,QAAM,KAAK,IAAI,sBAAAC,QAAS,QAAQ,EAAE,UAAU,KAAK,CAAC;AAElD,MAAI;AACF,UAAM,QAAQ,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAIxB,EAAE,IAAI;AAEP,UAAM,SAAS,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAIzB,EAAE,IAAI;AAEP,UAAM,QAAQ,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAEvD,YAAQ,IAAI,8BAAuB;AACnC,YAAQ,IAAI,YAAY;AACxB,YAAQ,IAAI,SAAI,OAAO,EAAE,CAAC;AAE1B,UAAM,cAAc,CAAC,SAAS,WAAW,QAAQ,UAAU,MAAM;AACjE,UAAM,cAAsC;AAAA,MAC1C,OAAO;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,MAAM;AAAA,IACR;AAEA,eAAW,UAAU,aAAa;AAChC,YAAM,OAAO,MAAM,KAAK,OAAK,EAAE,WAAW,MAAM;AAChD,YAAM,QAAQ,MAAM,SAAS;AAC7B,YAAM,QAAQ,YAAY,MAAM,KAAK;AACrC,cAAQ,IAAI,KAAK,KAAK,IAAI,OAAO,OAAO,EAAE,CAAC,IAAI,MAAM,eAAe,EAAE,SAAS,CAAC,CAAC,EAAE;AAAA,IACrF;AACA,YAAQ,IAAI,SAAI,OAAO,EAAE,CAAC;AAC1B,YAAQ,IAAI,mBAAmB,MAAM,eAAe,EAAE,SAAS,CAAC,CAAC,EAAE;AAEnE,QAAI,OAAO,SAAS,GAAG;AACrB,cAAQ,IAAI,aAAa;AACzB,cAAQ,IAAI,SAAI,OAAO,EAAE,CAAC;AAC1B,iBAAW,KAAK,QAAQ;AACtB,gBAAQ,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,eAAe,EAAE,SAAS,CAAC,CAAC,EAAE;AAAA,MAC/E;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,EACd,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,QAAQ,QAAgB,SAAgD;AACrF,QAAM,KAAK,IAAI,sBAAAA,QAAS,QAAQ,EAAE,UAAU,KAAK,CAAC;AAElD,MAAI;AACF,QAAI,QAAQ;AACZ,UAAM,SAAgB,CAAC;AAEvB,QAAI,QAAQ,QAAQ;AAClB,eAAS;AACT,aAAO,KAAK,QAAQ,MAAM;AAAA,IAC5B;AACA,QAAI,QAAQ,OAAO;AACjB,eAAS;AACT,aAAO,KAAK,QAAQ,KAAK;AAAA,IAC3B;AAEA,aAAS;AACT,WAAO,KAAK,SAAS,QAAQ,SAAS,OAAO,EAAE,CAAC;AAEhD,UAAM,OAAO,GAAG,QAAQ,KAAK,EAAE,IAAI,GAAG,MAAM;AAE5C,QAAI,KAAK,WAAW,GAAG;AACrB,cAAQ,IAAI,gBAAgB;AAC5B;AAAA,IACF;AAEA,YAAQ,IAAI,4EAA4E;AACxF,YAAQ,IAAI,SAAI,OAAO,EAAE,CAAC;AAE1B,eAAW,OAAO,MAAM;AACtB,YAAM,KAAK,IAAI,UAAU,MAAM,GAAG,EAAE;AACpC,YAAM,QAAQ,IAAI,MAAM,MAAM,GAAG,EAAE,EAAE,OAAO,EAAE;AAC9C,YAAM,SAAS,IAAI,OAAO,OAAO,CAAC;AAClC,YAAM,MAAM,OAAO,IAAI,QAAQ,EAAE,SAAS,CAAC;AAC3C,YAAM,WAAW,GAAG,IAAI,QAAQ,IAAI,IAAI,YAAY,GAAG,SAAS,CAAC;AACjE,YAAM,UAAU,IAAI,KAAK,IAAI,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAElE,cAAQ,IAAI,GAAG,EAAE,KAAK,KAAK,IAAI,MAAM,IAAI,GAAG,KAAK,QAAQ,KAAK,OAAO,EAAE;AAAA,IACzE;AAEA,YAAQ,IAAI;AAAA,UAAa,KAAK,MAAM;AAAA,CAAS;AAAA,EAC/C,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,UAAU,QAAgB,YAAgC,SAAgD;AACvH,QAAM,KAAK,IAAI,sBAAAA,QAAS,QAAQ,EAAE,UAAU,KAAK,CAAC;AAElD,MAAI;AACF,QAAI,QAAQ;AACZ,UAAM,SAAgB,CAAC;AAEvB,QAAI,QAAQ,QAAQ;AAClB,eAAS;AACT,aAAO,KAAK,QAAQ,MAAM;AAAA,IAC5B;AACA,QAAI,QAAQ,OAAO;AACjB,eAAS;AACT,aAAO,KAAK,QAAQ,KAAK;AAAA,IAC3B;AAEA,aAAS;AAET,UAAM,OAAO,GAAG,QAAQ,KAAK,EAAE,IAAI,GAAG,MAAM;AAE5C,UAAM,SAAS,iBAAa,6BAAkB,UAAU,IAAI,QAAQ;AAGpE,UAAM,UAAU;AAAA,MACd;AAAA,MAAa;AAAA,MAAS;AAAA,MAAW;AAAA,MAAU;AAAA,MAC3C;AAAA,MAAU;AAAA,MAAY;AAAA,MAAgB;AAAA,MACtC;AAAA,MAAc;AAAA,MAAc;AAAA,IAC9B;AAEA,QAAI,YAAY;AACd,aAAO,MAAM,QAAQ,KAAK,GAAG,IAAI,IAAI;AAAA,IACvC,OAAO;AACL,cAAQ,IAAI,QAAQ,KAAK,GAAG,CAAC;AAAA,IAC/B;AAGA,eAAW,OAAO,MAAM;AACtB,YAAM,MAAM,QAAQ,IAAI,SAAO,UAAU,IAAI,GAAG,CAAC,CAAC;AAClD,UAAI,YAAY;AACd,eAAO,MAAM,IAAI,KAAK,GAAG,IAAI,IAAI;AAAA,MACnC,OAAO;AACL,gBAAQ,IAAI,IAAI,KAAK,GAAG,CAAC;AAAA,MAC3B;AAAA,IACF;AAEA,QAAI,YAAY;AACd,MAAC,OAAe,IAAI;AACpB,cAAQ,MAAM,mBAAc,KAAK,MAAM,YAAY,UAAU,EAAE;AAAA,IACjE;AAAA,EACF,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,UAAU,QAAgB,WAAkC;AACzE,QAAM,KAAK,IAAI,sBAAAA,QAAS,MAAM;AAG9B,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAgBP;AAED,QAAM,SAAS,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,GAIzB;AAED,QAAM,SAAK,iCAAgB;AAAA,IACzB,WAAO,4BAAiB,SAAS;AAAA,IACjC,WAAW;AAAA,EACb,CAAC;AAED,MAAI,UAAU;AACd,MAAI,WAAW;AACf,MAAI,UAAoB,CAAC;AAEzB,MAAI;AACF,UAAM,aAAa,GAAG,YAAY,CAAC,SAAgB;AACjD,iBAAW,OAAO,MAAM;AACtB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,SAAS,IAAI,UAAU,EAAE,KAAK;AAAA,UAC9B,SAAS,IAAI,QAAQ,EAAE,KAAK,KAAK,IAAI;AAAA,UACrC,SAAS,IAAI,UAAU,EAAE,KAAK;AAAA,UAC9B,SAAS,IAAI,cAAc,EAAE,KAAK;AAAA,UAClC,SAAS,IAAI,aAAa,EAAE,KAAK;AAAA,UACjC,SAAS,IAAI,YAAY,EAAE,KAAK,KAAK,IAAI;AAAA,UACzC,SAAS,IAAI,YAAY,EAAE,KAAK,KAAK,IAAI;AAAA,UACzC,IAAI,cAAc;AAAA,QACpB;AACA;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,QAAe,CAAC;AAEtB,qBAAiB,QAAQ,IAAI;AAC3B;AAEA,UAAI,YAAY,GAAG;AAEjB,kBAAU,aAAa,IAAI;AAC3B;AAAA,MACF;AAEA,UAAI,CAAC,KAAK,KAAK,EAAG;AAElB,YAAM,SAAS,aAAa,IAAI;AAChC,YAAM,MAA8B,CAAC;AAErC,eAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAI,QAAQ,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK;AAAA,MACjC;AAEA,YAAM,KAAK,GAAG;AAGd,UAAI,MAAM,UAAU,KAAM;AACxB,mBAAW,KAAK;AAChB,cAAM,SAAS;AACf,gBAAQ,OAAO,MAAM,cAAc,QAAQ,UAAU;AAAA,MACvD;AAAA,IACF;AAGA,QAAI,MAAM,SAAS,GAAG;AACpB,iBAAW,KAAK;AAAA,IAClB;AAEA,YAAQ,IAAI;AAAA,kBAAgB,QAAQ,cAAc,SAAS,EAAE;AAAA,EAC/D,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,SAAS,QAAgB,SAAgD;AACtF,MAAI,CAAC,QAAQ,QAAQ;AACnB,YAAQ,MAAM,+CAA+C;AAC7D,YAAQ,MAAM,kDAAkD;AAChE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,KAAK,IAAI,sBAAAA,QAAS,MAAM;AAE9B,MAAI;AACF,UAAM,SAAS,GAAG,QAAQ,6CAA6C,EAAE,IAAI,QAAQ,MAAM;AAC3F,YAAQ,IAAI,4BAAgB,OAAO,OAAO,sBAAsB,QAAQ,MAAM,GAAG;AAAA,EACnF,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,SAAS,QAAgB,SAAgD;AACtF,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,KAAK,IAAI,sBAAAA,QAAS,MAAM;AAE9B,MAAI;AACF,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAQzB,EAAE,IAAI,KAAK,KAAK,MAAM;AAEvB,YAAQ,IAAI,mBAAY,OAAO,OAAO,eAAe,MAAM,cAAc;AAAA,EAC3E,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,SAAS,QAAgB,WAAkC;AACxE,QAAM,KAAK,IAAI,sBAAAA,QAAS,MAAM;AAE9B,MAAI;AAEF,OAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMP;AAED,UAAM,MAAM,KAAK,IAAI;AACrB,OAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAIV,EAAE,IAAI,WAAW,KAAK,GAAG;AAE1B,YAAQ,IAAI,+BAAqB,SAAS,GAAG;AAC7C,YAAQ,IAAI,iDAAiD;AAAA,EAC/D,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,UAAU,QAAgB,WAAkC;AACzE,QAAM,KAAK,IAAI,sBAAAA,QAAS,MAAM;AAE9B,MAAI;AAEF,OAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMP;AAED,UAAM,MAAM,KAAK,IAAI;AACrB,OAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAIV,EAAE,IAAI,WAAW,KAAK,GAAG;AAE1B,YAAQ,IAAI,gCAAsB,SAAS,GAAG;AAC9C,YAAQ,IAAI,4CAA4C;AAAA,EAC1D,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,UAAU,QAA+B;AACtD,QAAM,KAAK,IAAI,sBAAAA,QAAS,QAAQ,EAAE,UAAU,KAAK,CAAC;AAElD,MAAI;AAEF,UAAM,SAAS,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAUzB,EAAE,IAAI;AAEP,QAAI,OAAO,WAAW,GAAG;AACvB,cAAQ,IAAI,kBAAkB;AAC9B;AAAA,IACF;AAEA,YAAQ,IAAI,sBAAe;AAC3B,YAAQ,IAAI,kDAAkD;AAC9D,YAAQ,IAAI,SAAI,OAAO,EAAE,CAAC;AAE1B,eAAW,KAAK,QAAQ;AACtB,YAAM,OAAO,EAAE,MAAM,OAAO,EAAE;AAC9B,YAAM,SAAS,EAAE,SAAS,yBAAe;AACzC,YAAM,QAAQ,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC;AACxC,YAAM,UAAU,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC;AAC5C,YAAM,QAAQ,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC;AAExC,cAAQ,IAAI,GAAG,IAAI,IAAI,MAAM,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE;AAAA,IACjE;AAEA,YAAQ,IAAI;AAAA,EACd,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAGA,eAAe,OAAsB;AACnC,MAAI,CAAC,WAAW,YAAY,YAAY,YAAY,MAAM;AACxD,eAAW;AACX,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,iBAAiB,kBAAkB,KAAK,MAAM,CAAC,CAAC;AACtD,QAAM,UAAU,aAAa,IAAI;AAEjC,QAAM,SAAS,eAAe,CAAC;AAE/B,MAAI,CAAC,QAAQ;AACX,YAAQ,MAAM,kCAAkC;AAChD,eAAW;AACX,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEE,MAAI;AACJ,YAAQ,SAAS;AAAA,MACf,KAAK;AACH,cAAM,SAAS,MAAM;AACrB;AAAA,MAEF,KAAK;AACH,cAAM,QAAQ,QAAQ,OAAO;AAC7B;AAAA,MAEF,KAAK;AACH,cAAM,UAAU,QAAQ,eAAe,CAAC,GAAG,OAAO;AAClD;AAAA,MAEF,KAAK;AACH,YAAI,CAAC,eAAe,CAAC,GAAG;AACtB,kBAAQ,MAAM,mCAAmC;AACjD,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,UAAU,QAAQ,eAAe,CAAC,CAAC;AACzC;AAAA,MAEF,KAAK;AACH,cAAM,SAAS,QAAQ,OAAO;AAC9B;AAAA,MAEF,KAAK;AACH,cAAM,SAAS,QAAQ,OAAO;AAC9B;AAAA,MAEF,KAAK;AACH,YAAI,CAAC,eAAe,CAAC,GAAG;AACtB,kBAAQ,MAAM,+BAA+B;AAC7C,kBAAQ,MAAM,2CAA2C;AACzD,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,SAAS,QAAQ,eAAe,CAAC,CAAC;AACxC;AAAA,MAEF,KAAK;AACH,YAAI,CAAC,eAAe,CAAC,GAAG;AACtB,kBAAQ,MAAM,+BAA+B;AAC7C,kBAAQ,MAAM,4CAA4C;AAC1D,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,UAAU,QAAQ,eAAe,CAAC,CAAC;AACzC;AAAA,MAEF,KAAK;AACH,cAAM,UAAU,MAAM;AACtB;AAAA,MAEF;AACE,gBAAQ,MAAM,oBAAoB,OAAO,EAAE;AAC3C,mBAAW;AACX,gBAAQ,KAAK,CAAC;AAAA,IAClB;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AACtE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,KAAK;","names":["args","Database"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Workmatic CLI\n * \n * Commands:\n * stats <db> Show job statistics\n * export <db> [output] Export jobs to CSV\n * import <db> <input> Import jobs from CSV\n * purge <db> [--status=done] Delete jobs by status\n * retry <db> [--status=dead] Retry failed/dead jobs\n * pause <db> <queue> Pause a queue (workers stop claiming)\n * resume <db> <queue> Resume a paused queue\n */\n\nimport Database from 'better-sqlite3';\nimport { createReadStream, createWriteStream } from 'fs';\nimport { createInterface } from 'readline';\nimport Table from 'cli-table3';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printUsage(): void {\n console.log(`\nWorkmatic CLI - Job Queue Management\n\nUsage:\n workmatic <command> [options]\n\nCommands:\n stats <db> Show job statistics\n queues <db> List all queues with status\n list <db> [--status=<status>] List jobs (optionally filter by status)\n export <db> [output.csv] Export jobs to CSV (default: stdout)\n import <db> <input.csv> Import jobs from CSV\n purge <db> --status=<status> Delete jobs by status\n retry <db> [--status=dead] Retry dead/failed jobs (reset to ready)\n pause <db> <queue> Pause a queue (running workers stop claiming)\n resume <db> <queue> Resume a paused queue\n\nOptions:\n --status=<status> Filter by status (ready|running|done|failed|dead)\n --queue=<queue> Filter by queue name\n --limit=<n> Limit number of results (default: 100)\n\nExamples:\n workmatic stats ./jobs.db\n workmatic list ./jobs.db --status=dead --limit=10\n workmatic export ./jobs.db backup.csv\n workmatic export ./jobs.db --status=failed > failed-jobs.csv\n workmatic import ./jobs.db backup.csv\n workmatic purge ./jobs.db --status=done\n workmatic retry ./jobs.db --status=dead\n workmatic pause ./jobs.db emails\n workmatic resume ./jobs.db emails\n`);\n}\n\nfunction parseOptions(args: string[]): Record<string, string> {\n const options: Record<string, string> = {};\n for (const arg of args) {\n if (arg.startsWith('--')) {\n const [key, value] = arg.slice(2).split('=');\n options[key] = value ?? 'true';\n }\n }\n return options;\n}\n\nfunction getPositionalArgs(args: string[]): string[] {\n return args.filter(arg => !arg.startsWith('--'));\n}\n\n// CSV helpers\nfunction escapeCSV(value: string | null): string {\n if (value === null) return '';\n const str = String(value);\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\nfunction parseCSVLine(line: string): string[] {\n const result: string[] = [];\n let current = '';\n let inQuotes = false;\n \n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n \n if (inQuotes) {\n if (char === '\"') {\n if (line[i + 1] === '\"') {\n current += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n current += char;\n }\n } else {\n if (char === '\"') {\n inQuotes = true;\n } else if (char === ',') {\n result.push(current);\n current = '';\n } else {\n current += char;\n }\n }\n }\n \n result.push(current);\n return result;\n}\n\n// Commands\nasync function cmdStats(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n const stats = db.prepare(`\n SELECT status, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY status\n `).all() as { status: string; count: number }[];\n \n const queues = db.prepare(`\n SELECT queue, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY queue\n `).all() as { queue: string; count: number }[];\n \n const total = stats.reduce((sum, s) => sum + s.count, 0);\n \n console.log('\\n📊 Job Statistics\\n');\n \n const statusOrder = ['ready', 'running', 'done', 'failed', 'dead'];\n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▶️',\n done: '✅',\n failed: '⚠️',\n dead: '💀',\n };\n \n const statusTable = new Table({\n head: ['', 'Status', 'Count'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right'],\n });\n \n for (const status of statusOrder) {\n const stat = stats.find(s => s.status === status);\n const count = stat?.count ?? 0;\n const emoji = statusEmoji[status] || '';\n statusTable.push([emoji, status, count.toLocaleString()]);\n }\n statusTable.push([{ colSpan: 2, content: 'Total', hAlign: 'right' }, total.toLocaleString()]);\n \n console.log(statusTable.toString());\n \n if (queues.length > 0) {\n console.log('\\n📋 By Queue\\n');\n \n const queueTable = new Table({\n head: ['Queue', 'Jobs'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'right'],\n });\n \n for (const q of queues) {\n queueTable.push([q.queue, q.count.toLocaleString()]);\n }\n \n console.log(queueTable.toString());\n }\n \n console.log();\n } finally {\n db.close();\n }\n}\n\nasync function cmdList(dbPath: string, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT public_id, queue, status, priority, attempts, max_attempts, created_at, last_error FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY created_at DESC LIMIT ?';\n params.push(parseInt(options.limit || '100', 10));\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n if (jobs.length === 0) {\n console.log('No jobs found.');\n return;\n }\n \n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▶️',\n done: '✅',\n failed: '⚠️',\n dead: '💀',\n };\n \n const table = new Table({\n head: ['ID', 'Queue', 'Status', 'Pri', 'Attempts', 'Created'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'left', 'right', 'center', 'left'],\n colWidths: [24, 16, 12, 5, 10, 21],\n wordWrap: true,\n });\n \n for (const job of jobs) {\n const emoji = statusEmoji[job.status] || '';\n table.push([\n job.public_id.slice(0, 21),\n job.queue.slice(0, 14),\n `${emoji} ${job.status}`,\n job.priority,\n `${job.attempts}/${job.max_attempts}`,\n new Date(job.created_at).toISOString().slice(0, 19),\n ]);\n }\n \n console.log();\n console.log(table.toString());\n console.log(`\\n Showing ${jobs.length} jobs\\n`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdExport(dbPath: string, outputPath: string | undefined, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT * FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY id';\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n const output = outputPath ? createWriteStream(outputPath) : process.stdout;\n \n // Header\n const columns = [\n 'public_id', 'queue', 'payload', 'status', 'priority',\n 'run_at', 'attempts', 'max_attempts', 'lease_until',\n 'created_at', 'updated_at', 'last_error'\n ];\n \n if (outputPath) {\n output.write(columns.join(',') + '\\n');\n } else {\n console.log(columns.join(','));\n }\n \n // Rows\n for (const job of jobs) {\n const row = columns.map(col => escapeCSV(job[col]));\n if (outputPath) {\n output.write(row.join(',') + '\\n');\n } else {\n console.log(row.join(','));\n }\n }\n \n if (outputPath) {\n (output as any).end();\n console.error(`✅ Exported ${jobs.length} jobs to ${outputPath}`);\n }\n } finally {\n db.close();\n }\n}\n\nasync function cmdImport(dbPath: string, inputPath: string): Promise<void> {\n const db = new Database(dbPath);\n \n // Ensure schema exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n public_id TEXT UNIQUE NOT NULL,\n queue TEXT NOT NULL,\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'ready',\n priority INTEGER NOT NULL DEFAULT 0,\n run_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n lease_until INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n last_error TEXT\n )\n `);\n \n const insert = db.prepare(`\n INSERT OR REPLACE INTO workmatic_jobs \n (public_id, queue, payload, status, priority, run_at, attempts, max_attempts, lease_until, created_at, updated_at, last_error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const rl = createInterface({\n input: createReadStream(inputPath),\n crlfDelay: Infinity,\n });\n \n let lineNum = 0;\n let imported = 0;\n let columns: string[] = [];\n \n try {\n const insertMany = db.transaction((rows: any[]) => {\n for (const row of rows) {\n insert.run(\n row.public_id,\n row.queue,\n row.payload,\n row.status,\n parseInt(row.priority, 10) || 0,\n parseInt(row.run_at, 10) || Date.now(),\n parseInt(row.attempts, 10) || 0,\n parseInt(row.max_attempts, 10) || 3,\n parseInt(row.lease_until, 10) || 0,\n parseInt(row.created_at, 10) || Date.now(),\n parseInt(row.updated_at, 10) || Date.now(),\n row.last_error || null\n );\n imported++;\n }\n });\n \n const batch: any[] = [];\n \n for await (const line of rl) {\n lineNum++;\n \n if (lineNum === 1) {\n // Header row\n columns = parseCSVLine(line);\n continue;\n }\n \n if (!line.trim()) continue;\n \n const values = parseCSVLine(line);\n const row: Record<string, string> = {};\n \n for (let i = 0; i < columns.length; i++) {\n row[columns[i]] = values[i] ?? '';\n }\n \n batch.push(row);\n \n // Batch insert every 1000 rows\n if (batch.length >= 1000) {\n insertMany(batch);\n batch.length = 0;\n process.stderr.write(`\\rImported ${imported} jobs...`);\n }\n }\n \n // Insert remaining\n if (batch.length > 0) {\n insertMany(batch);\n }\n \n console.log(`\\n✅ Imported ${imported} jobs from ${inputPath}`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPurge(dbPath: string, options: Record<string, string>): Promise<void> {\n if (!options.status) {\n console.error('Error: --status is required for purge command');\n console.error('Example: workmatic purge ./jobs.db --status=done');\n process.exit(1);\n }\n \n const db = new Database(dbPath);\n \n try {\n const result = db.prepare('DELETE FROM workmatic_jobs WHERE status = ?').run(options.status);\n console.log(`🗑️ Deleted ${result.changes} jobs with status '${options.status}'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdRetry(dbPath: string, options: Record<string, string>): Promise<void> {\n const status = options.status || 'dead';\n const db = new Database(dbPath);\n \n try {\n const now = Date.now();\n const result = db.prepare(`\n UPDATE workmatic_jobs \n SET status = 'ready', \n attempts = 0, \n run_at = ?, \n lease_until = 0,\n updated_at = ?\n WHERE status = ?\n `).run(now, now, status);\n \n console.log(`🔄 Reset ${result.changes} jobs from '${status}' to 'ready'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPause(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 1, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 1, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`⏸️ Paused queue '${queueName}'`);\n console.log(` Running workers will stop claiming new jobs.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdResume(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 0, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 0, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`▶️ Resumed queue '${queueName}'`);\n console.log(` Workers will start claiming jobs again.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdQueues(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n // Check if settings table exists\n const settingsExists = db.prepare(`\n SELECT name FROM sqlite_master WHERE type='table' AND name='workmatic_settings'\n `).get();\n \n // Get all queues with their job counts and pause state\n let queues: { queue: string; total: number; ready: number; running: number; paused: number }[];\n \n if (settingsExists) {\n queues = db.prepare(`\n SELECT \n j.queue,\n COUNT(*) as total,\n SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,\n COALESCE(s.paused, 0) as paused\n FROM workmatic_jobs j\n LEFT JOIN workmatic_settings s ON j.queue = s.queue\n GROUP BY j.queue\n `).all() as typeof queues;\n } else {\n queues = db.prepare(`\n SELECT \n queue,\n COUNT(*) as total,\n SUM(CASE WHEN status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running,\n 0 as paused\n FROM workmatic_jobs\n GROUP BY queue\n `).all() as typeof queues;\n }\n \n if (queues.length === 0) {\n console.log('No queues found.');\n return;\n }\n \n console.log('\\n📋 Queues\\n');\n \n const table = new Table({\n head: ['Queue', 'Status', 'Ready', 'Running', 'Total'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right', 'right', 'right'],\n });\n \n for (const q of queues) {\n const status = q.paused ? '⏸️ PAUSED' : '▶️ ACTIVE';\n table.push([\n q.queue,\n status,\n q.ready.toLocaleString(),\n q.running.toLocaleString(),\n q.total.toLocaleString(),\n ]);\n }\n \n console.log(table.toString());\n console.log();\n } finally {\n db.close();\n }\n}\n\n// Main\nasync function main(): Promise<void> {\n if (!command || command === '--help' || command === '-h') {\n printUsage();\n process.exit(0);\n }\n \n const positionalArgs = getPositionalArgs(args.slice(1));\n const options = parseOptions(args);\n \n const dbPath = positionalArgs[0];\n \n if (!dbPath) {\n console.error('Error: Database path is required');\n printUsage();\n process.exit(1);\n }\n \n try {\n switch (command) {\n case 'stats':\n await cmdStats(dbPath);\n break;\n \n case 'list':\n await cmdList(dbPath, options);\n break;\n \n case 'export':\n await cmdExport(dbPath, positionalArgs[1], options);\n break;\n \n case 'import':\n if (!positionalArgs[1]) {\n console.error('Error: Input CSV file is required');\n process.exit(1);\n }\n await cmdImport(dbPath, positionalArgs[1]);\n break;\n \n case 'purge':\n await cmdPurge(dbPath, options);\n break;\n \n case 'retry':\n await cmdRetry(dbPath, options);\n break;\n \n case 'pause':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic pause ./jobs.db emails');\n process.exit(1);\n }\n await cmdPause(dbPath, positionalArgs[1]);\n break;\n \n case 'resume':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic resume ./jobs.db emails');\n process.exit(1);\n }\n await cmdResume(dbPath, positionalArgs[1]);\n break;\n \n case 'queues':\n await cmdQueues(dbPath);\n break;\n \n default:\n console.error(`Unknown command: ${command}`);\n printUsage();\n process.exit(1);\n }\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":["args","Database","Table"]}
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import Database from "better-sqlite3";
|
|
5
5
|
import { createReadStream, createWriteStream } from "fs";
|
|
6
6
|
import { createInterface } from "readline";
|
|
7
|
+
import Table from "cli-table3";
|
|
7
8
|
var args = process.argv.slice(2);
|
|
8
9
|
var command = args[0];
|
|
9
10
|
function printUsage() {
|
|
@@ -108,30 +109,38 @@ async function cmdStats(dbPath) {
|
|
|
108
109
|
`).all();
|
|
109
110
|
const total = stats.reduce((sum, s) => sum + s.count, 0);
|
|
110
111
|
console.log("\n\u{1F4CA} Job Statistics\n");
|
|
111
|
-
console.log("By Status:");
|
|
112
|
-
console.log("\u2500".repeat(30));
|
|
113
112
|
const statusOrder = ["ready", "running", "done", "failed", "dead"];
|
|
114
113
|
const statusEmoji = {
|
|
115
114
|
ready: "\u23F3",
|
|
116
|
-
running: "\u25B6\uFE0F
|
|
115
|
+
running: "\u25B6\uFE0F",
|
|
117
116
|
done: "\u2705",
|
|
118
|
-
failed: "\u26A0\uFE0F
|
|
117
|
+
failed: "\u26A0\uFE0F",
|
|
119
118
|
dead: "\u{1F480}"
|
|
120
119
|
};
|
|
120
|
+
const statusTable = new Table({
|
|
121
|
+
head: ["", "Status", "Count"],
|
|
122
|
+
style: { head: ["cyan"], border: ["gray"] },
|
|
123
|
+
colAligns: ["left", "left", "right"]
|
|
124
|
+
});
|
|
121
125
|
for (const status of statusOrder) {
|
|
122
126
|
const stat = stats.find((s) => s.status === status);
|
|
123
127
|
const count = stat?.count ?? 0;
|
|
124
|
-
const emoji = statusEmoji[status] || "
|
|
125
|
-
|
|
128
|
+
const emoji = statusEmoji[status] || "";
|
|
129
|
+
statusTable.push([emoji, status, count.toLocaleString()]);
|
|
126
130
|
}
|
|
127
|
-
|
|
128
|
-
console.log(
|
|
131
|
+
statusTable.push([{ colSpan: 2, content: "Total", hAlign: "right" }, total.toLocaleString()]);
|
|
132
|
+
console.log(statusTable.toString());
|
|
129
133
|
if (queues.length > 0) {
|
|
130
|
-
console.log("\
|
|
131
|
-
|
|
134
|
+
console.log("\n\u{1F4CB} By Queue\n");
|
|
135
|
+
const queueTable = new Table({
|
|
136
|
+
head: ["Queue", "Jobs"],
|
|
137
|
+
style: { head: ["cyan"], border: ["gray"] },
|
|
138
|
+
colAligns: ["left", "right"]
|
|
139
|
+
});
|
|
132
140
|
for (const q of queues) {
|
|
133
|
-
|
|
141
|
+
queueTable.push([q.queue, q.count.toLocaleString()]);
|
|
134
142
|
}
|
|
143
|
+
console.log(queueTable.toString());
|
|
135
144
|
}
|
|
136
145
|
console.log();
|
|
137
146
|
} finally {
|
|
@@ -158,19 +167,35 @@ async function cmdList(dbPath, options) {
|
|
|
158
167
|
console.log("No jobs found.");
|
|
159
168
|
return;
|
|
160
169
|
}
|
|
161
|
-
|
|
162
|
-
|
|
170
|
+
const statusEmoji = {
|
|
171
|
+
ready: "\u23F3",
|
|
172
|
+
running: "\u25B6\uFE0F",
|
|
173
|
+
done: "\u2705",
|
|
174
|
+
failed: "\u26A0\uFE0F",
|
|
175
|
+
dead: "\u{1F480}"
|
|
176
|
+
};
|
|
177
|
+
const table = new Table({
|
|
178
|
+
head: ["ID", "Queue", "Status", "Pri", "Attempts", "Created"],
|
|
179
|
+
style: { head: ["cyan"], border: ["gray"] },
|
|
180
|
+
colAligns: ["left", "left", "left", "right", "center", "left"],
|
|
181
|
+
colWidths: [24, 16, 12, 5, 10, 21],
|
|
182
|
+
wordWrap: true
|
|
183
|
+
});
|
|
163
184
|
for (const job of jobs) {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
185
|
+
const emoji = statusEmoji[job.status] || "";
|
|
186
|
+
table.push([
|
|
187
|
+
job.public_id.slice(0, 21),
|
|
188
|
+
job.queue.slice(0, 14),
|
|
189
|
+
`${emoji} ${job.status}`,
|
|
190
|
+
job.priority,
|
|
191
|
+
`${job.attempts}/${job.max_attempts}`,
|
|
192
|
+
new Date(job.created_at).toISOString().slice(0, 19)
|
|
193
|
+
]);
|
|
171
194
|
}
|
|
195
|
+
console.log();
|
|
196
|
+
console.log(table.toString());
|
|
172
197
|
console.log(`
|
|
173
|
-
Showing ${jobs.length} jobs
|
|
198
|
+
Showing ${jobs.length} jobs
|
|
174
199
|
`);
|
|
175
200
|
} finally {
|
|
176
201
|
db.close();
|
|
@@ -387,32 +412,55 @@ async function cmdResume(dbPath, queueName) {
|
|
|
387
412
|
async function cmdQueues(dbPath) {
|
|
388
413
|
const db = new Database(dbPath, { readonly: true });
|
|
389
414
|
try {
|
|
390
|
-
const
|
|
391
|
-
SELECT
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
415
|
+
const settingsExists = db.prepare(`
|
|
416
|
+
SELECT name FROM sqlite_master WHERE type='table' AND name='workmatic_settings'
|
|
417
|
+
`).get();
|
|
418
|
+
let queues;
|
|
419
|
+
if (settingsExists) {
|
|
420
|
+
queues = db.prepare(`
|
|
421
|
+
SELECT
|
|
422
|
+
j.queue,
|
|
423
|
+
COUNT(*) as total,
|
|
424
|
+
SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,
|
|
425
|
+
SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,
|
|
426
|
+
COALESCE(s.paused, 0) as paused
|
|
427
|
+
FROM workmatic_jobs j
|
|
428
|
+
LEFT JOIN workmatic_settings s ON j.queue = s.queue
|
|
429
|
+
GROUP BY j.queue
|
|
430
|
+
`).all();
|
|
431
|
+
} else {
|
|
432
|
+
queues = db.prepare(`
|
|
433
|
+
SELECT
|
|
434
|
+
queue,
|
|
435
|
+
COUNT(*) as total,
|
|
436
|
+
SUM(CASE WHEN status = 'ready' THEN 1 ELSE 0 END) as ready,
|
|
437
|
+
SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running,
|
|
438
|
+
0 as paused
|
|
439
|
+
FROM workmatic_jobs
|
|
440
|
+
GROUP BY queue
|
|
441
|
+
`).all();
|
|
442
|
+
}
|
|
401
443
|
if (queues.length === 0) {
|
|
402
444
|
console.log("No queues found.");
|
|
403
445
|
return;
|
|
404
446
|
}
|
|
405
447
|
console.log("\n\u{1F4CB} Queues\n");
|
|
406
|
-
|
|
407
|
-
|
|
448
|
+
const table = new Table({
|
|
449
|
+
head: ["Queue", "Status", "Ready", "Running", "Total"],
|
|
450
|
+
style: { head: ["cyan"], border: ["gray"] },
|
|
451
|
+
colAligns: ["left", "left", "right", "right", "right"]
|
|
452
|
+
});
|
|
408
453
|
for (const q of queues) {
|
|
409
|
-
const name = q.queue.padEnd(16);
|
|
410
454
|
const status = q.paused ? "\u23F8\uFE0F PAUSED" : "\u25B6\uFE0F ACTIVE";
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
455
|
+
table.push([
|
|
456
|
+
q.queue,
|
|
457
|
+
status,
|
|
458
|
+
q.ready.toLocaleString(),
|
|
459
|
+
q.running.toLocaleString(),
|
|
460
|
+
q.total.toLocaleString()
|
|
461
|
+
]);
|
|
415
462
|
}
|
|
463
|
+
console.log(table.toString());
|
|
416
464
|
console.log();
|
|
417
465
|
} finally {
|
|
418
466
|
db.close();
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Workmatic CLI\n * \n * Commands:\n * stats <db> Show job statistics\n * export <db> [output] Export jobs to CSV\n * import <db> <input> Import jobs from CSV\n * purge <db> [--status=done] Delete jobs by status\n * retry <db> [--status=dead] Retry failed/dead jobs\n * pause <db> <queue> Pause a queue (workers stop claiming)\n * resume <db> <queue> Resume a paused queue\n */\n\nimport Database from 'better-sqlite3';\nimport { createReadStream, createWriteStream } from 'fs';\nimport { createInterface } from 'readline';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printUsage(): void {\n console.log(`\nWorkmatic CLI - Job Queue Management\n\nUsage:\n workmatic <command> [options]\n\nCommands:\n stats <db> Show job statistics\n queues <db> List all queues with status\n list <db> [--status=<status>] List jobs (optionally filter by status)\n export <db> [output.csv] Export jobs to CSV (default: stdout)\n import <db> <input.csv> Import jobs from CSV\n purge <db> --status=<status> Delete jobs by status\n retry <db> [--status=dead] Retry dead/failed jobs (reset to ready)\n pause <db> <queue> Pause a queue (running workers stop claiming)\n resume <db> <queue> Resume a paused queue\n\nOptions:\n --status=<status> Filter by status (ready|running|done|failed|dead)\n --queue=<queue> Filter by queue name\n --limit=<n> Limit number of results (default: 100)\n\nExamples:\n workmatic stats ./jobs.db\n workmatic list ./jobs.db --status=dead --limit=10\n workmatic export ./jobs.db backup.csv\n workmatic export ./jobs.db --status=failed > failed-jobs.csv\n workmatic import ./jobs.db backup.csv\n workmatic purge ./jobs.db --status=done\n workmatic retry ./jobs.db --status=dead\n workmatic pause ./jobs.db emails\n workmatic resume ./jobs.db emails\n`);\n}\n\nfunction parseOptions(args: string[]): Record<string, string> {\n const options: Record<string, string> = {};\n for (const arg of args) {\n if (arg.startsWith('--')) {\n const [key, value] = arg.slice(2).split('=');\n options[key] = value ?? 'true';\n }\n }\n return options;\n}\n\nfunction getPositionalArgs(args: string[]): string[] {\n return args.filter(arg => !arg.startsWith('--'));\n}\n\n// CSV helpers\nfunction escapeCSV(value: string | null): string {\n if (value === null) return '';\n const str = String(value);\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\nfunction parseCSVLine(line: string): string[] {\n const result: string[] = [];\n let current = '';\n let inQuotes = false;\n \n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n \n if (inQuotes) {\n if (char === '\"') {\n if (line[i + 1] === '\"') {\n current += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n current += char;\n }\n } else {\n if (char === '\"') {\n inQuotes = true;\n } else if (char === ',') {\n result.push(current);\n current = '';\n } else {\n current += char;\n }\n }\n }\n \n result.push(current);\n return result;\n}\n\n// Commands\nasync function cmdStats(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n const stats = db.prepare(`\n SELECT status, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY status\n `).all() as { status: string; count: number }[];\n \n const queues = db.prepare(`\n SELECT queue, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY queue\n `).all() as { queue: string; count: number }[];\n \n const total = stats.reduce((sum, s) => sum + s.count, 0);\n \n console.log('\\n📊 Job Statistics\\n');\n console.log('By Status:');\n console.log('─'.repeat(30));\n \n const statusOrder = ['ready', 'running', 'done', 'failed', 'dead'];\n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▶️ ',\n done: '✅',\n failed: '⚠️ ',\n dead: '💀',\n };\n \n for (const status of statusOrder) {\n const stat = stats.find(s => s.status === status);\n const count = stat?.count ?? 0;\n const emoji = statusEmoji[status] || ' ';\n console.log(` ${emoji} ${status.padEnd(10)} ${count.toLocaleString().padStart(8)}`);\n }\n console.log('─'.repeat(30));\n console.log(` Total: ${total.toLocaleString().padStart(8)}`);\n \n if (queues.length > 0) {\n console.log('\\nBy Queue:');\n console.log('─'.repeat(30));\n for (const q of queues) {\n console.log(` ${q.queue.padEnd(15)} ${q.count.toLocaleString().padStart(8)}`);\n }\n }\n \n console.log();\n } finally {\n db.close();\n }\n}\n\nasync function cmdList(dbPath: string, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT public_id, queue, status, priority, attempts, max_attempts, created_at, last_error FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY created_at DESC LIMIT ?';\n params.push(parseInt(options.limit || '100', 10));\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n if (jobs.length === 0) {\n console.log('No jobs found.');\n return;\n }\n \n console.log('\\nID Queue Status Pri Attempts Created');\n console.log('─'.repeat(85));\n \n for (const job of jobs) {\n const id = job.public_id.slice(0, 21);\n const queue = job.queue.slice(0, 15).padEnd(15);\n const status = job.status.padEnd(9);\n const pri = String(job.priority).padStart(3);\n const attempts = `${job.attempts}/${job.max_attempts}`.padStart(8);\n const created = new Date(job.created_at).toISOString().slice(0, 19);\n \n console.log(`${id} ${queue} ${status} ${pri} ${attempts} ${created}`);\n }\n \n console.log(`\\nShowing ${jobs.length} jobs\\n`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdExport(dbPath: string, outputPath: string | undefined, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT * FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY id';\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n const output = outputPath ? createWriteStream(outputPath) : process.stdout;\n \n // Header\n const columns = [\n 'public_id', 'queue', 'payload', 'status', 'priority',\n 'run_at', 'attempts', 'max_attempts', 'lease_until',\n 'created_at', 'updated_at', 'last_error'\n ];\n \n if (outputPath) {\n output.write(columns.join(',') + '\\n');\n } else {\n console.log(columns.join(','));\n }\n \n // Rows\n for (const job of jobs) {\n const row = columns.map(col => escapeCSV(job[col]));\n if (outputPath) {\n output.write(row.join(',') + '\\n');\n } else {\n console.log(row.join(','));\n }\n }\n \n if (outputPath) {\n (output as any).end();\n console.error(`✅ Exported ${jobs.length} jobs to ${outputPath}`);\n }\n } finally {\n db.close();\n }\n}\n\nasync function cmdImport(dbPath: string, inputPath: string): Promise<void> {\n const db = new Database(dbPath);\n \n // Ensure schema exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n public_id TEXT UNIQUE NOT NULL,\n queue TEXT NOT NULL,\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'ready',\n priority INTEGER NOT NULL DEFAULT 0,\n run_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n lease_until INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n last_error TEXT\n )\n `);\n \n const insert = db.prepare(`\n INSERT OR REPLACE INTO workmatic_jobs \n (public_id, queue, payload, status, priority, run_at, attempts, max_attempts, lease_until, created_at, updated_at, last_error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const rl = createInterface({\n input: createReadStream(inputPath),\n crlfDelay: Infinity,\n });\n \n let lineNum = 0;\n let imported = 0;\n let columns: string[] = [];\n \n try {\n const insertMany = db.transaction((rows: any[]) => {\n for (const row of rows) {\n insert.run(\n row.public_id,\n row.queue,\n row.payload,\n row.status,\n parseInt(row.priority, 10) || 0,\n parseInt(row.run_at, 10) || Date.now(),\n parseInt(row.attempts, 10) || 0,\n parseInt(row.max_attempts, 10) || 3,\n parseInt(row.lease_until, 10) || 0,\n parseInt(row.created_at, 10) || Date.now(),\n parseInt(row.updated_at, 10) || Date.now(),\n row.last_error || null\n );\n imported++;\n }\n });\n \n const batch: any[] = [];\n \n for await (const line of rl) {\n lineNum++;\n \n if (lineNum === 1) {\n // Header row\n columns = parseCSVLine(line);\n continue;\n }\n \n if (!line.trim()) continue;\n \n const values = parseCSVLine(line);\n const row: Record<string, string> = {};\n \n for (let i = 0; i < columns.length; i++) {\n row[columns[i]] = values[i] ?? '';\n }\n \n batch.push(row);\n \n // Batch insert every 1000 rows\n if (batch.length >= 1000) {\n insertMany(batch);\n batch.length = 0;\n process.stderr.write(`\\rImported ${imported} jobs...`);\n }\n }\n \n // Insert remaining\n if (batch.length > 0) {\n insertMany(batch);\n }\n \n console.log(`\\n✅ Imported ${imported} jobs from ${inputPath}`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPurge(dbPath: string, options: Record<string, string>): Promise<void> {\n if (!options.status) {\n console.error('Error: --status is required for purge command');\n console.error('Example: workmatic purge ./jobs.db --status=done');\n process.exit(1);\n }\n \n const db = new Database(dbPath);\n \n try {\n const result = db.prepare('DELETE FROM workmatic_jobs WHERE status = ?').run(options.status);\n console.log(`🗑️ Deleted ${result.changes} jobs with status '${options.status}'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdRetry(dbPath: string, options: Record<string, string>): Promise<void> {\n const status = options.status || 'dead';\n const db = new Database(dbPath);\n \n try {\n const now = Date.now();\n const result = db.prepare(`\n UPDATE workmatic_jobs \n SET status = 'ready', \n attempts = 0, \n run_at = ?, \n lease_until = 0,\n updated_at = ?\n WHERE status = ?\n `).run(now, now, status);\n \n console.log(`🔄 Reset ${result.changes} jobs from '${status}' to 'ready'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPause(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 1, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 1, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`⏸️ Paused queue '${queueName}'`);\n console.log(` Running workers will stop claiming new jobs.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdResume(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 0, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 0, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`▶️ Resumed queue '${queueName}'`);\n console.log(` Workers will start claiming jobs again.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdQueues(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n // Get all queues with their job counts and pause state\n const queues = db.prepare(`\n SELECT \n j.queue,\n COUNT(*) as total,\n SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,\n COALESCE(s.paused, 0) as paused\n FROM workmatic_jobs j\n LEFT JOIN workmatic_settings s ON j.queue = s.queue\n GROUP BY j.queue\n `).all() as { queue: string; total: number; ready: number; running: number; paused: number }[];\n \n if (queues.length === 0) {\n console.log('No queues found.');\n return;\n }\n \n console.log('\\n📋 Queues\\n');\n console.log('Queue Status Ready Running Total');\n console.log('─'.repeat(55));\n \n for (const q of queues) {\n const name = q.queue.padEnd(16);\n const status = q.paused ? '⏸️ PAUSED' : '▶️ ACTIVE';\n const ready = String(q.ready).padStart(5);\n const running = String(q.running).padStart(8);\n const total = String(q.total).padStart(6);\n \n console.log(`${name} ${status} ${ready} ${running} ${total}`);\n }\n \n console.log();\n } finally {\n db.close();\n }\n}\n\n// Main\nasync function main(): Promise<void> {\n if (!command || command === '--help' || command === '-h') {\n printUsage();\n process.exit(0);\n }\n \n const positionalArgs = getPositionalArgs(args.slice(1));\n const options = parseOptions(args);\n \n const dbPath = positionalArgs[0];\n \n if (!dbPath) {\n console.error('Error: Database path is required');\n printUsage();\n process.exit(1);\n }\n \n try {\n switch (command) {\n case 'stats':\n await cmdStats(dbPath);\n break;\n \n case 'list':\n await cmdList(dbPath, options);\n break;\n \n case 'export':\n await cmdExport(dbPath, positionalArgs[1], options);\n break;\n \n case 'import':\n if (!positionalArgs[1]) {\n console.error('Error: Input CSV file is required');\n process.exit(1);\n }\n await cmdImport(dbPath, positionalArgs[1]);\n break;\n \n case 'purge':\n await cmdPurge(dbPath, options);\n break;\n \n case 'retry':\n await cmdRetry(dbPath, options);\n break;\n \n case 'pause':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic pause ./jobs.db emails');\n process.exit(1);\n }\n await cmdPause(dbPath, positionalArgs[1]);\n break;\n \n case 'resume':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic resume ./jobs.db emails');\n process.exit(1);\n }\n await cmdResume(dbPath, positionalArgs[1]);\n break;\n \n case 'queues':\n await cmdQueues(dbPath);\n break;\n \n default:\n console.error(`Unknown command: ${command}`);\n printUsage();\n process.exit(1);\n }\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":["args"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Workmatic CLI\n * \n * Commands:\n * stats <db> Show job statistics\n * export <db> [output] Export jobs to CSV\n * import <db> <input> Import jobs from CSV\n * purge <db> [--status=done] Delete jobs by status\n * retry <db> [--status=dead] Retry failed/dead jobs\n * pause <db> <queue> Pause a queue (workers stop claiming)\n * resume <db> <queue> Resume a paused queue\n */\n\nimport Database from 'better-sqlite3';\nimport { createReadStream, createWriteStream } from 'fs';\nimport { createInterface } from 'readline';\nimport Table from 'cli-table3';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printUsage(): void {\n console.log(`\nWorkmatic CLI - Job Queue Management\n\nUsage:\n workmatic <command> [options]\n\nCommands:\n stats <db> Show job statistics\n queues <db> List all queues with status\n list <db> [--status=<status>] List jobs (optionally filter by status)\n export <db> [output.csv] Export jobs to CSV (default: stdout)\n import <db> <input.csv> Import jobs from CSV\n purge <db> --status=<status> Delete jobs by status\n retry <db> [--status=dead] Retry dead/failed jobs (reset to ready)\n pause <db> <queue> Pause a queue (running workers stop claiming)\n resume <db> <queue> Resume a paused queue\n\nOptions:\n --status=<status> Filter by status (ready|running|done|failed|dead)\n --queue=<queue> Filter by queue name\n --limit=<n> Limit number of results (default: 100)\n\nExamples:\n workmatic stats ./jobs.db\n workmatic list ./jobs.db --status=dead --limit=10\n workmatic export ./jobs.db backup.csv\n workmatic export ./jobs.db --status=failed > failed-jobs.csv\n workmatic import ./jobs.db backup.csv\n workmatic purge ./jobs.db --status=done\n workmatic retry ./jobs.db --status=dead\n workmatic pause ./jobs.db emails\n workmatic resume ./jobs.db emails\n`);\n}\n\nfunction parseOptions(args: string[]): Record<string, string> {\n const options: Record<string, string> = {};\n for (const arg of args) {\n if (arg.startsWith('--')) {\n const [key, value] = arg.slice(2).split('=');\n options[key] = value ?? 'true';\n }\n }\n return options;\n}\n\nfunction getPositionalArgs(args: string[]): string[] {\n return args.filter(arg => !arg.startsWith('--'));\n}\n\n// CSV helpers\nfunction escapeCSV(value: string | null): string {\n if (value === null) return '';\n const str = String(value);\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\nfunction parseCSVLine(line: string): string[] {\n const result: string[] = [];\n let current = '';\n let inQuotes = false;\n \n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n \n if (inQuotes) {\n if (char === '\"') {\n if (line[i + 1] === '\"') {\n current += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n current += char;\n }\n } else {\n if (char === '\"') {\n inQuotes = true;\n } else if (char === ',') {\n result.push(current);\n current = '';\n } else {\n current += char;\n }\n }\n }\n \n result.push(current);\n return result;\n}\n\n// Commands\nasync function cmdStats(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n const stats = db.prepare(`\n SELECT status, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY status\n `).all() as { status: string; count: number }[];\n \n const queues = db.prepare(`\n SELECT queue, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY queue\n `).all() as { queue: string; count: number }[];\n \n const total = stats.reduce((sum, s) => sum + s.count, 0);\n \n console.log('\\n📊 Job Statistics\\n');\n \n const statusOrder = ['ready', 'running', 'done', 'failed', 'dead'];\n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▶️',\n done: '✅',\n failed: '⚠️',\n dead: '💀',\n };\n \n const statusTable = new Table({\n head: ['', 'Status', 'Count'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right'],\n });\n \n for (const status of statusOrder) {\n const stat = stats.find(s => s.status === status);\n const count = stat?.count ?? 0;\n const emoji = statusEmoji[status] || '';\n statusTable.push([emoji, status, count.toLocaleString()]);\n }\n statusTable.push([{ colSpan: 2, content: 'Total', hAlign: 'right' }, total.toLocaleString()]);\n \n console.log(statusTable.toString());\n \n if (queues.length > 0) {\n console.log('\\n📋 By Queue\\n');\n \n const queueTable = new Table({\n head: ['Queue', 'Jobs'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'right'],\n });\n \n for (const q of queues) {\n queueTable.push([q.queue, q.count.toLocaleString()]);\n }\n \n console.log(queueTable.toString());\n }\n \n console.log();\n } finally {\n db.close();\n }\n}\n\nasync function cmdList(dbPath: string, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT public_id, queue, status, priority, attempts, max_attempts, created_at, last_error FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY created_at DESC LIMIT ?';\n params.push(parseInt(options.limit || '100', 10));\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n if (jobs.length === 0) {\n console.log('No jobs found.');\n return;\n }\n \n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▶️',\n done: '✅',\n failed: '⚠️',\n dead: '💀',\n };\n \n const table = new Table({\n head: ['ID', 'Queue', 'Status', 'Pri', 'Attempts', 'Created'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'left', 'right', 'center', 'left'],\n colWidths: [24, 16, 12, 5, 10, 21],\n wordWrap: true,\n });\n \n for (const job of jobs) {\n const emoji = statusEmoji[job.status] || '';\n table.push([\n job.public_id.slice(0, 21),\n job.queue.slice(0, 14),\n `${emoji} ${job.status}`,\n job.priority,\n `${job.attempts}/${job.max_attempts}`,\n new Date(job.created_at).toISOString().slice(0, 19),\n ]);\n }\n \n console.log();\n console.log(table.toString());\n console.log(`\\n Showing ${jobs.length} jobs\\n`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdExport(dbPath: string, outputPath: string | undefined, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT * FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY id';\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n const output = outputPath ? createWriteStream(outputPath) : process.stdout;\n \n // Header\n const columns = [\n 'public_id', 'queue', 'payload', 'status', 'priority',\n 'run_at', 'attempts', 'max_attempts', 'lease_until',\n 'created_at', 'updated_at', 'last_error'\n ];\n \n if (outputPath) {\n output.write(columns.join(',') + '\\n');\n } else {\n console.log(columns.join(','));\n }\n \n // Rows\n for (const job of jobs) {\n const row = columns.map(col => escapeCSV(job[col]));\n if (outputPath) {\n output.write(row.join(',') + '\\n');\n } else {\n console.log(row.join(','));\n }\n }\n \n if (outputPath) {\n (output as any).end();\n console.error(`✅ Exported ${jobs.length} jobs to ${outputPath}`);\n }\n } finally {\n db.close();\n }\n}\n\nasync function cmdImport(dbPath: string, inputPath: string): Promise<void> {\n const db = new Database(dbPath);\n \n // Ensure schema exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n public_id TEXT UNIQUE NOT NULL,\n queue TEXT NOT NULL,\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'ready',\n priority INTEGER NOT NULL DEFAULT 0,\n run_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n lease_until INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n last_error TEXT\n )\n `);\n \n const insert = db.prepare(`\n INSERT OR REPLACE INTO workmatic_jobs \n (public_id, queue, payload, status, priority, run_at, attempts, max_attempts, lease_until, created_at, updated_at, last_error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const rl = createInterface({\n input: createReadStream(inputPath),\n crlfDelay: Infinity,\n });\n \n let lineNum = 0;\n let imported = 0;\n let columns: string[] = [];\n \n try {\n const insertMany = db.transaction((rows: any[]) => {\n for (const row of rows) {\n insert.run(\n row.public_id,\n row.queue,\n row.payload,\n row.status,\n parseInt(row.priority, 10) || 0,\n parseInt(row.run_at, 10) || Date.now(),\n parseInt(row.attempts, 10) || 0,\n parseInt(row.max_attempts, 10) || 3,\n parseInt(row.lease_until, 10) || 0,\n parseInt(row.created_at, 10) || Date.now(),\n parseInt(row.updated_at, 10) || Date.now(),\n row.last_error || null\n );\n imported++;\n }\n });\n \n const batch: any[] = [];\n \n for await (const line of rl) {\n lineNum++;\n \n if (lineNum === 1) {\n // Header row\n columns = parseCSVLine(line);\n continue;\n }\n \n if (!line.trim()) continue;\n \n const values = parseCSVLine(line);\n const row: Record<string, string> = {};\n \n for (let i = 0; i < columns.length; i++) {\n row[columns[i]] = values[i] ?? '';\n }\n \n batch.push(row);\n \n // Batch insert every 1000 rows\n if (batch.length >= 1000) {\n insertMany(batch);\n batch.length = 0;\n process.stderr.write(`\\rImported ${imported} jobs...`);\n }\n }\n \n // Insert remaining\n if (batch.length > 0) {\n insertMany(batch);\n }\n \n console.log(`\\n✅ Imported ${imported} jobs from ${inputPath}`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPurge(dbPath: string, options: Record<string, string>): Promise<void> {\n if (!options.status) {\n console.error('Error: --status is required for purge command');\n console.error('Example: workmatic purge ./jobs.db --status=done');\n process.exit(1);\n }\n \n const db = new Database(dbPath);\n \n try {\n const result = db.prepare('DELETE FROM workmatic_jobs WHERE status = ?').run(options.status);\n console.log(`🗑️ Deleted ${result.changes} jobs with status '${options.status}'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdRetry(dbPath: string, options: Record<string, string>): Promise<void> {\n const status = options.status || 'dead';\n const db = new Database(dbPath);\n \n try {\n const now = Date.now();\n const result = db.prepare(`\n UPDATE workmatic_jobs \n SET status = 'ready', \n attempts = 0, \n run_at = ?, \n lease_until = 0,\n updated_at = ?\n WHERE status = ?\n `).run(now, now, status);\n \n console.log(`🔄 Reset ${result.changes} jobs from '${status}' to 'ready'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPause(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 1, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 1, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`⏸️ Paused queue '${queueName}'`);\n console.log(` Running workers will stop claiming new jobs.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdResume(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 0, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 0, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`▶️ Resumed queue '${queueName}'`);\n console.log(` Workers will start claiming jobs again.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdQueues(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n // Check if settings table exists\n const settingsExists = db.prepare(`\n SELECT name FROM sqlite_master WHERE type='table' AND name='workmatic_settings'\n `).get();\n \n // Get all queues with their job counts and pause state\n let queues: { queue: string; total: number; ready: number; running: number; paused: number }[];\n \n if (settingsExists) {\n queues = db.prepare(`\n SELECT \n j.queue,\n COUNT(*) as total,\n SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,\n COALESCE(s.paused, 0) as paused\n FROM workmatic_jobs j\n LEFT JOIN workmatic_settings s ON j.queue = s.queue\n GROUP BY j.queue\n `).all() as typeof queues;\n } else {\n queues = db.prepare(`\n SELECT \n queue,\n COUNT(*) as total,\n SUM(CASE WHEN status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running,\n 0 as paused\n FROM workmatic_jobs\n GROUP BY queue\n `).all() as typeof queues;\n }\n \n if (queues.length === 0) {\n console.log('No queues found.');\n return;\n }\n \n console.log('\\n📋 Queues\\n');\n \n const table = new Table({\n head: ['Queue', 'Status', 'Ready', 'Running', 'Total'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right', 'right', 'right'],\n });\n \n for (const q of queues) {\n const status = q.paused ? '⏸️ PAUSED' : '▶️ ACTIVE';\n table.push([\n q.queue,\n status,\n q.ready.toLocaleString(),\n q.running.toLocaleString(),\n q.total.toLocaleString(),\n ]);\n }\n \n console.log(table.toString());\n console.log();\n } finally {\n db.close();\n }\n}\n\n// Main\nasync function main(): Promise<void> {\n if (!command || command === '--help' || command === '-h') {\n printUsage();\n process.exit(0);\n }\n \n const positionalArgs = getPositionalArgs(args.slice(1));\n const options = parseOptions(args);\n \n const dbPath = positionalArgs[0];\n \n if (!dbPath) {\n console.error('Error: Database path is required');\n printUsage();\n process.exit(1);\n }\n \n try {\n switch (command) {\n case 'stats':\n await cmdStats(dbPath);\n break;\n \n case 'list':\n await cmdList(dbPath, options);\n break;\n \n case 'export':\n await cmdExport(dbPath, positionalArgs[1], options);\n break;\n \n case 'import':\n if (!positionalArgs[1]) {\n console.error('Error: Input CSV file is required');\n process.exit(1);\n }\n await cmdImport(dbPath, positionalArgs[1]);\n break;\n \n case 'purge':\n await cmdPurge(dbPath, options);\n break;\n \n case 'retry':\n await cmdRetry(dbPath, options);\n break;\n \n case 'pause':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic pause ./jobs.db emails');\n process.exit(1);\n }\n await cmdPause(dbPath, positionalArgs[1]);\n break;\n \n case 'resume':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic resume ./jobs.db emails');\n process.exit(1);\n }\n await cmdResume(dbPath, positionalArgs[1]);\n break;\n \n case 'queues':\n await cmdQueues(dbPath);\n break;\n \n default:\n console.error(`Unknown command: ${command}`);\n printUsage();\n process.exit(1);\n }\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":["args"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "workmatic",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A persistent job queue for Node.js using SQLite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -38,10 +38,19 @@
|
|
|
38
38
|
"background-jobs",
|
|
39
39
|
"worker"
|
|
40
40
|
],
|
|
41
|
-
"author": "",
|
|
41
|
+
"author": "cond",
|
|
42
42
|
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/litepacks/workmatic.git"
|
|
46
|
+
},
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/litepacks/workmatic/issues"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/litepacks/workmatic#readme",
|
|
43
51
|
"dependencies": {
|
|
44
52
|
"better-sqlite3": "^11.7.0",
|
|
53
|
+
"cli-table3": "^0.6.5",
|
|
45
54
|
"fastq": "^1.18.0",
|
|
46
55
|
"kysely": "^0.27.5",
|
|
47
56
|
"nanoid": "^5.0.9"
|