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 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
- console.log(` ${emoji} ${status.padEnd(10)} ${count.toLocaleString().padStart(8)}`);
151
+ const emoji = statusEmoji[status] || "";
152
+ statusTable.push([emoji, status, count.toLocaleString()]);
149
153
  }
150
- console.log("\u2500".repeat(30));
151
- console.log(` Total: ${total.toLocaleString().padStart(8)}`);
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("\nBy Queue:");
154
- console.log("\u2500".repeat(30));
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
- console.log(` ${q.queue.padEnd(15)} ${q.count.toLocaleString().padStart(8)}`);
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
- console.log("\nID Queue Status Pri Attempts Created");
185
- console.log("\u2500".repeat(85));
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 id = job.public_id.slice(0, 21);
188
- const queue = job.queue.slice(0, 15).padEnd(15);
189
- const status = job.status.padEnd(9);
190
- const pri = String(job.priority).padStart(3);
191
- const attempts = `${job.attempts}/${job.max_attempts}`.padStart(8);
192
- const created = new Date(job.created_at).toISOString().slice(0, 19);
193
- console.log(`${id} ${queue} ${status} ${pri} ${attempts} ${created}`);
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 queues = db.prepare(`
414
- SELECT
415
- j.queue,
416
- COUNT(*) as total,
417
- SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,
418
- SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,
419
- COALESCE(s.paused, 0) as paused
420
- FROM workmatic_jobs j
421
- LEFT JOIN workmatic_settings s ON j.queue = s.queue
422
- GROUP BY j.queue
423
- `).all();
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
- console.log("Queue Status Ready Running Total");
430
- console.log("\u2500".repeat(55));
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
- const ready = String(q.ready).padStart(5);
435
- const running = String(q.running).padStart(8);
436
- const total = String(q.total).padStart(6);
437
- console.log(`${name} ${status} ${ready} ${running} ${total}`);
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":"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","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
- console.log(` ${emoji} ${status.padEnd(10)} ${count.toLocaleString().padStart(8)}`);
128
+ const emoji = statusEmoji[status] || "";
129
+ statusTable.push([emoji, status, count.toLocaleString()]);
126
130
  }
127
- console.log("\u2500".repeat(30));
128
- console.log(` Total: ${total.toLocaleString().padStart(8)}`);
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("\nBy Queue:");
131
- console.log("\u2500".repeat(30));
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
- console.log(` ${q.queue.padEnd(15)} ${q.count.toLocaleString().padStart(8)}`);
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
- console.log("\nID Queue Status Pri Attempts Created");
162
- console.log("\u2500".repeat(85));
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 id = job.public_id.slice(0, 21);
165
- const queue = job.queue.slice(0, 15).padEnd(15);
166
- const status = job.status.padEnd(9);
167
- const pri = String(job.priority).padStart(3);
168
- const attempts = `${job.attempts}/${job.max_attempts}`.padStart(8);
169
- const created = new Date(job.created_at).toISOString().slice(0, 19);
170
- console.log(`${id} ${queue} ${status} ${pri} ${attempts} ${created}`);
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 queues = db.prepare(`
391
- SELECT
392
- j.queue,
393
- COUNT(*) as total,
394
- SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,
395
- SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,
396
- COALESCE(s.paused, 0) as paused
397
- FROM workmatic_jobs j
398
- LEFT JOIN workmatic_settings s ON j.queue = s.queue
399
- GROUP BY j.queue
400
- `).all();
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
- console.log("Queue Status Ready Running Total");
407
- console.log("\u2500".repeat(55));
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
- const ready = String(q.ready).padStart(5);
412
- const running = String(q.running).padStart(8);
413
- const total = String(q.total).padStart(6);
414
- console.log(`${name} ${status} ${ready} ${running} ${total}`);
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.1",
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"