workmatic 1.0.4 β 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -13
- package/dashboard/app.js +1 -3
- package/dashboard/index.html +0 -14
- package/dist/cli.cjs +4 -6
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +136 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -6
- package/dist/index.d.ts +49 -6
- package/dist/index.js +134 -25
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A persistent job queue for Node.js using SQLite. Simple, reliable, and zero external dependencies beyond SQLite.
|
|
4
4
|
|
|
5
|
+
**Full single-page reference (same material, browsable offline): [`docs/index.html`](docs/index.html)**.
|
|
6
|
+
|
|
5
7
|
## Why?
|
|
6
8
|
|
|
7
9
|
I love [fastq](https://github.com/mcollina/fastq) - it's fast, simple, and has a great API. But it's in-memory only, which can be frustrating when you need jobs to survive process restarts or crashes.
|
|
@@ -63,11 +65,15 @@ worker.start();
|
|
|
63
65
|
|
|
64
66
|
## API Reference
|
|
65
67
|
|
|
68
|
+
Browsable copy with anchored sections: **[`docs/index.html`](docs/index.html)** (API from [`#api-database`](docs/index.html#api-database)).
|
|
69
|
+
|
|
66
70
|
### `createDatabase(options)`
|
|
67
71
|
|
|
68
72
|
Initialize the database connection and schema.
|
|
69
73
|
|
|
70
74
|
```typescript
|
|
75
|
+
import { createDatabase, getUnderlyingDb } from 'workmatic';
|
|
76
|
+
|
|
71
77
|
const db = createDatabase({
|
|
72
78
|
// Option 1: File path (creates or opens existing)
|
|
73
79
|
filename: './jobs.db',
|
|
@@ -78,6 +84,9 @@ const db = createDatabase({
|
|
|
78
84
|
// Option 3: Existing better-sqlite3 instance
|
|
79
85
|
db: existingSqliteInstance,
|
|
80
86
|
});
|
|
87
|
+
|
|
88
|
+
// Underlying driver (only reliable for instances from createDatabase above)
|
|
89
|
+
const sqlite = getUnderlyingDb(db);
|
|
81
90
|
```
|
|
82
91
|
|
|
83
92
|
### `createClient(options)`
|
|
@@ -112,7 +121,32 @@ Get job statistics for the queue.
|
|
|
112
121
|
|
|
113
122
|
```typescript
|
|
114
123
|
const stats = await client.stats();
|
|
115
|
-
// { ready: 5, running: 2, done: 100,
|
|
124
|
+
// { ready: 5, running: 2, done: 100, dead: 1, total: 108 }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### `client.addMany(payloads, options?)`
|
|
128
|
+
|
|
129
|
+
Insert many jobs in a **single transaction**, sharing the same `priority`, `delayMs`, and `maxAttempts`.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const { ok, ids } = await client.addMany(
|
|
133
|
+
[{ email: 'a@x.com' }, { email: 'b@x.com' }],
|
|
134
|
+
{ priority: 1, delayMs: 0, maxAttempts: 3 }
|
|
135
|
+
);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### `client.clear(options?)`
|
|
139
|
+
|
|
140
|
+
Clear all jobs from the queue.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// Clear all jobs
|
|
144
|
+
const deleted = await client.clear();
|
|
145
|
+
console.log(`Deleted ${deleted} jobs`);
|
|
146
|
+
|
|
147
|
+
// Clear only jobs with specific status
|
|
148
|
+
const deleted = await client.clear({ status: 'done' });
|
|
149
|
+
console.log(`Deleted ${deleted} done jobs`);
|
|
116
150
|
```
|
|
117
151
|
|
|
118
152
|
### `createWorker(options)`
|
|
@@ -126,10 +160,13 @@ const worker = createWorker({
|
|
|
126
160
|
concurrency: 1, // Optional: Parallel job count (default: 1)
|
|
127
161
|
leaseMs: 30000, // Optional: Job lease duration in ms (default: 30000)
|
|
128
162
|
pollMs: 1000, // Optional: Poll interval when idle (default: 1000)
|
|
129
|
-
timeoutMs: 60000, // Optional: Job execution timeout in ms (default:
|
|
163
|
+
timeoutMs: 60000, // Optional: Job execution timeout in ms (default: 60000). Use 0 for no limit
|
|
130
164
|
backoff: (n) => 1000 * Math.pow(2, n), // Optional: Retry backoff function
|
|
131
165
|
persistState: false, // Optional: Persist worker state to database (default: false)
|
|
132
166
|
autoRestore: true, // Optional: Auto-restore state on creation (default: true)
|
|
167
|
+
pauseCheckIntervalMs: 300, // Optional: Min ms between DB pause checks (CLI pause). Default: 300
|
|
168
|
+
requeueExpiredIntervalMs: 0, // Optional: Min ms between lease requeue scans; 0 = every pump
|
|
169
|
+
onPumpError: (err) => { /* optional hook after default log */ },
|
|
133
170
|
});
|
|
134
171
|
```
|
|
135
172
|
|
|
@@ -181,6 +218,20 @@ Get job statistics for the queue.
|
|
|
181
218
|
const stats = await worker.stats();
|
|
182
219
|
```
|
|
183
220
|
|
|
221
|
+
#### `worker.clear(options?)`
|
|
222
|
+
|
|
223
|
+
Clear all jobs from the queue.
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
// Clear all jobs in the queue
|
|
227
|
+
const deleted = await worker.clear();
|
|
228
|
+
console.log(`Deleted ${deleted} jobs`);
|
|
229
|
+
|
|
230
|
+
// Clear only jobs with specific status
|
|
231
|
+
const deleted = await worker.clear({ status: 'dead' });
|
|
232
|
+
console.log(`Deleted ${deleted} dead jobs`);
|
|
233
|
+
```
|
|
234
|
+
|
|
184
235
|
#### Worker properties
|
|
185
236
|
|
|
186
237
|
```typescript
|
|
@@ -302,7 +353,7 @@ interface Job<TPayload> {
|
|
|
302
353
|
id: string; // Unique public ID (nanoid)
|
|
303
354
|
queue: string; // Queue name
|
|
304
355
|
payload: TPayload; // Your job data
|
|
305
|
-
status: JobStatus; // 'ready' | 'running' | 'done' | '
|
|
356
|
+
status: JobStatus; // 'ready' | 'running' | 'done' | 'dead' while running
|
|
306
357
|
priority: number; // Priority value
|
|
307
358
|
attempts: number; // Current attempt count (starts at 0)
|
|
308
359
|
maxAttempts: number; // Maximum attempts allowed
|
|
@@ -378,11 +429,14 @@ worker.process(async (job) => {
|
|
|
378
429
|
| `concurrency` | `1` | Number of jobs to process in parallel |
|
|
379
430
|
| `leaseMs` | `30000` | How long a job is "locked" during processing |
|
|
380
431
|
| `pollMs` | `1000` | How often to check for new jobs when idle |
|
|
381
|
-
| `timeoutMs` | `
|
|
432
|
+
| `timeoutMs` | `60000` | Job timeout in ms (`0` = no limit) |
|
|
382
433
|
| `priority` | `0` | Job priority (lower = processed first) |
|
|
383
434
|
| `delayMs` | `0` | Delay before job becomes available |
|
|
384
435
|
| `maxAttempts` | `3` | Maximum processing attempts |
|
|
385
436
|
| `backoff` | `2^n * 1000` | Function returning retry delay in ms |
|
|
437
|
+
| `pauseCheckIntervalMs` | `300` | Throttle CLI/live pause checks from the pump loop |
|
|
438
|
+
| `requeueExpiredIntervalMs` | `0` | Throttle expired-lease requeue (0 = run every pump tick) |
|
|
439
|
+
| `onPumpError` | `undefined` | Callback after the default pump error log |
|
|
386
440
|
|
|
387
441
|
## Dashboard
|
|
388
442
|
|
|
@@ -422,6 +476,25 @@ npm run bench
|
|
|
422
476
|
npm run bench -- --file
|
|
423
477
|
```
|
|
424
478
|
|
|
479
|
+
### Micro benchmarks
|
|
480
|
+
|
|
481
|
+
Short suite: **2,000 sequential `add()` calls** (same code path as the full insert benchmark, smaller batch) plus **1,000 `client.stats()` calls** on a queue that already holds jobs. Useful for quick regression checks without running the full workload.
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
npm run bench:micro
|
|
485
|
+
# or: npm run bench -- --micro
|
|
486
|
+
|
|
487
|
+
npm run bench:micro -- --file
|
|
488
|
+
# or: npm run bench -- --micro --file
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
| Benchmark | In-Memory | File-based |
|
|
492
|
+
|-----------|-----------|------------|
|
|
493
|
+
| Micro Sequential Insert (2,000) | ~27,000/s | ~9,300/s |
|
|
494
|
+
| Stats Query (Γ1,000) | ~9,200/s | ~5,500/s |
|
|
495
|
+
|
|
496
|
+
Figures are rounded from a representative run; throughput changes with hardware, SQLite settings, and how βwarmβ the database is.
|
|
497
|
+
|
|
425
498
|
### Results Comparison
|
|
426
499
|
|
|
427
500
|
| Benchmark | In-Memory | File-based |
|
|
@@ -433,6 +506,8 @@ npm run bench -- --file
|
|
|
433
506
|
| Process (concurrency=8) | 10,000/s | 8,300/s |
|
|
434
507
|
| Process (concurrency=16) | 18,000/s | 5,700/s |
|
|
435
508
|
| Mixed Insert+Process | 7,500/s | 3,500/s |
|
|
509
|
+
| Micro Sequential Insert (2,000) | ~27,000/s | ~9,300/s |
|
|
510
|
+
| Stats Query (Γ1,000) | ~9,200/s | ~5,500/s |
|
|
436
511
|
| Claim + Process Batch | 23,600/s | 11,800/s |
|
|
437
512
|
|
|
438
513
|
**Note**: File-based performance degrades at high concurrency due to disk I/O. For file-based databases, `concurrency=8` is often optimal. Performance varies by hardware.
|
|
@@ -457,15 +532,9 @@ npx workmatic list ./jobs.db --status=dead --limit=10
|
|
|
457
532
|
|
|
458
533
|
# Export jobs to CSV
|
|
459
534
|
npx workmatic export ./jobs.db backup.csv
|
|
460
|
-
npx workmatic export ./jobs.db --status=
|
|
461
|
-
|
|
462
|
-
# Import jobs from CSV
|
|
463
|
-
npx workmatic import ./jobs.db backup.csv
|
|
464
|
-
|
|
465
|
-
# Delete jobs by status
|
|
466
|
-
npx workmatic purge ./jobs.db --status=done
|
|
535
|
+
npx workmatic export ./jobs.db --status=dead > dead-export.csv
|
|
467
536
|
|
|
468
|
-
# Retry dead
|
|
537
|
+
# Retry dead jobs (reset to ready)
|
|
469
538
|
npx workmatic retry ./jobs.db --status=dead
|
|
470
539
|
```
|
|
471
540
|
|
|
@@ -487,7 +556,7 @@ npx workmatic retry ./jobs.db --status=dead
|
|
|
487
556
|
|
|
488
557
|
| Option | Description |
|
|
489
558
|
|--------|-------------|
|
|
490
|
-
| `--status=<status>` | Filter by status (ready/running/done/
|
|
559
|
+
| `--status=<status>` | Filter by status (ready/running/done/dead) |
|
|
491
560
|
| `--queue=<queue>` | Filter by queue name |
|
|
492
561
|
| `--limit=<n>` | Limit results (default: 100) |
|
|
493
562
|
|
package/dashboard/app.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Dashboard state
|
|
2
2
|
const state = {
|
|
3
|
-
stats: { ready: 0, running: 0, done: 0,
|
|
3
|
+
stats: { ready: 0, running: 0, done: 0, dead: 0, total: 0 },
|
|
4
4
|
jobs: [],
|
|
5
5
|
queues: [],
|
|
6
6
|
workers: [],
|
|
@@ -18,7 +18,6 @@ const elements = {
|
|
|
18
18
|
statReady: document.getElementById('stat-ready'),
|
|
19
19
|
statRunning: document.getElementById('stat-running'),
|
|
20
20
|
statDone: document.getElementById('stat-done'),
|
|
21
|
-
statFailed: document.getElementById('stat-failed'),
|
|
22
21
|
statDead: document.getElementById('stat-dead'),
|
|
23
22
|
workersSection: document.getElementById('workers-section'),
|
|
24
23
|
workersGrid: document.getElementById('workers-grid'),
|
|
@@ -95,7 +94,6 @@ function updateStatsUI() {
|
|
|
95
94
|
elements.statReady.textContent = state.stats.ready.toLocaleString();
|
|
96
95
|
elements.statRunning.textContent = state.stats.running.toLocaleString();
|
|
97
96
|
elements.statDone.textContent = state.stats.done.toLocaleString();
|
|
98
|
-
elements.statFailed.textContent = state.stats.failed.toLocaleString();
|
|
99
97
|
elements.statDead.textContent = state.stats.dead.toLocaleString();
|
|
100
98
|
}
|
|
101
99
|
|
package/dashboard/index.html
CHANGED
|
@@ -60,19 +60,6 @@
|
|
|
60
60
|
<span class="stat-label">Done</span>
|
|
61
61
|
</div>
|
|
62
62
|
</div>
|
|
63
|
-
<div class="stat-card failed">
|
|
64
|
-
<div class="stat-icon">
|
|
65
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
66
|
-
<circle cx="12" cy="12" r="10"/>
|
|
67
|
-
<line x1="12" y1="8" x2="12" y2="12"/>
|
|
68
|
-
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
|
69
|
-
</svg>
|
|
70
|
-
</div>
|
|
71
|
-
<div class="stat-content">
|
|
72
|
-
<span class="stat-value" id="stat-failed">0</span>
|
|
73
|
-
<span class="stat-label">Failed</span>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
63
|
<div class="stat-card dead">
|
|
77
64
|
<div class="stat-icon">
|
|
78
65
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
@@ -105,7 +92,6 @@
|
|
|
105
92
|
<option value="ready">Ready</option>
|
|
106
93
|
<option value="running">Running</option>
|
|
107
94
|
<option value="done">Done</option>
|
|
108
|
-
<option value="failed">Failed</option>
|
|
109
95
|
<option value="dead">Dead</option>
|
|
110
96
|
</select>
|
|
111
97
|
</div>
|
package/dist/cli.cjs
CHANGED
|
@@ -44,12 +44,12 @@ Commands:
|
|
|
44
44
|
export <db> [output.csv] Export jobs to CSV (default: stdout)
|
|
45
45
|
import <db> <input.csv> Import jobs from CSV
|
|
46
46
|
purge <db> --status=<status> Delete jobs by status
|
|
47
|
-
retry <db> [--status=dead] Retry dead
|
|
47
|
+
retry <db> [--status=dead] Retry dead jobs (reset to ready)
|
|
48
48
|
pause <db> <queue> Pause a queue (running workers stop claiming)
|
|
49
49
|
resume <db> <queue> Resume a paused queue
|
|
50
50
|
|
|
51
51
|
Options:
|
|
52
|
-
--status=<status> Filter by status (ready|running|done|
|
|
52
|
+
--status=<status> Filter by status (ready|running|done|dead)
|
|
53
53
|
--queue=<queue> Filter by queue name
|
|
54
54
|
--limit=<n> Limit number of results (default: 100)
|
|
55
55
|
|
|
@@ -57,7 +57,7 @@ Examples:
|
|
|
57
57
|
workmatic stats ./jobs.db
|
|
58
58
|
workmatic list ./jobs.db --status=dead --limit=10
|
|
59
59
|
workmatic export ./jobs.db backup.csv
|
|
60
|
-
workmatic export ./jobs.db --status=
|
|
60
|
+
workmatic export ./jobs.db --status=dead > dead-jobs.csv
|
|
61
61
|
workmatic import ./jobs.db backup.csv
|
|
62
62
|
workmatic purge ./jobs.db --status=done
|
|
63
63
|
workmatic retry ./jobs.db --status=dead
|
|
@@ -132,12 +132,11 @@ async function cmdStats(dbPath) {
|
|
|
132
132
|
`).all();
|
|
133
133
|
const total = stats.reduce((sum, s) => sum + s.count, 0);
|
|
134
134
|
console.log("\n\u{1F4CA} Job Statistics\n");
|
|
135
|
-
const statusOrder = ["ready", "running", "done", "
|
|
135
|
+
const statusOrder = ["ready", "running", "done", "dead"];
|
|
136
136
|
const statusEmoji = {
|
|
137
137
|
ready: "\u23F3",
|
|
138
138
|
running: "\u25B6\uFE0F",
|
|
139
139
|
done: "\u2705",
|
|
140
|
-
failed: "\u26A0\uFE0F",
|
|
141
140
|
dead: "\u{1F480}"
|
|
142
141
|
};
|
|
143
142
|
const statusTable = new import_cli_table3.default({
|
|
@@ -194,7 +193,6 @@ async function cmdList(dbPath, options) {
|
|
|
194
193
|
ready: "\u23F3",
|
|
195
194
|
running: "\u25B6\uFE0F",
|
|
196
195
|
done: "\u2705",
|
|
197
|
-
failed: "\u26A0\uFE0F",
|
|
198
196
|
dead: "\u{1F480}"
|
|
199
197
|
};
|
|
200
198
|
const table = new import_cli_table3.default({
|
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';\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"]}
|
|
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 dead jobs (reset to ready)\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 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|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=dead > dead-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', 'dead'];\n const statusEmoji: Record<string, string> = {\n ready: 'β³',\n running: 'βΆοΈ',\n done: 'β
',\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 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
|
@@ -21,12 +21,12 @@ Commands:
|
|
|
21
21
|
export <db> [output.csv] Export jobs to CSV (default: stdout)
|
|
22
22
|
import <db> <input.csv> Import jobs from CSV
|
|
23
23
|
purge <db> --status=<status> Delete jobs by status
|
|
24
|
-
retry <db> [--status=dead] Retry dead
|
|
24
|
+
retry <db> [--status=dead] Retry dead jobs (reset to ready)
|
|
25
25
|
pause <db> <queue> Pause a queue (running workers stop claiming)
|
|
26
26
|
resume <db> <queue> Resume a paused queue
|
|
27
27
|
|
|
28
28
|
Options:
|
|
29
|
-
--status=<status> Filter by status (ready|running|done|
|
|
29
|
+
--status=<status> Filter by status (ready|running|done|dead)
|
|
30
30
|
--queue=<queue> Filter by queue name
|
|
31
31
|
--limit=<n> Limit number of results (default: 100)
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@ Examples:
|
|
|
34
34
|
workmatic stats ./jobs.db
|
|
35
35
|
workmatic list ./jobs.db --status=dead --limit=10
|
|
36
36
|
workmatic export ./jobs.db backup.csv
|
|
37
|
-
workmatic export ./jobs.db --status=
|
|
37
|
+
workmatic export ./jobs.db --status=dead > dead-jobs.csv
|
|
38
38
|
workmatic import ./jobs.db backup.csv
|
|
39
39
|
workmatic purge ./jobs.db --status=done
|
|
40
40
|
workmatic retry ./jobs.db --status=dead
|
|
@@ -109,12 +109,11 @@ async function cmdStats(dbPath) {
|
|
|
109
109
|
`).all();
|
|
110
110
|
const total = stats.reduce((sum, s) => sum + s.count, 0);
|
|
111
111
|
console.log("\n\u{1F4CA} Job Statistics\n");
|
|
112
|
-
const statusOrder = ["ready", "running", "done", "
|
|
112
|
+
const statusOrder = ["ready", "running", "done", "dead"];
|
|
113
113
|
const statusEmoji = {
|
|
114
114
|
ready: "\u23F3",
|
|
115
115
|
running: "\u25B6\uFE0F",
|
|
116
116
|
done: "\u2705",
|
|
117
|
-
failed: "\u26A0\uFE0F",
|
|
118
117
|
dead: "\u{1F480}"
|
|
119
118
|
};
|
|
120
119
|
const statusTable = new Table({
|
|
@@ -171,7 +170,6 @@ async function cmdList(dbPath, options) {
|
|
|
171
170
|
ready: "\u23F3",
|
|
172
171
|
running: "\u25B6\uFE0F",
|
|
173
172
|
done: "\u2705",
|
|
174
|
-
failed: "\u26A0\uFE0F",
|
|
175
173
|
dead: "\u{1F480}"
|
|
176
174
|
};
|
|
177
175
|
const table = new Table({
|
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';\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"]}
|
|
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 dead jobs (reset to ready)\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 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|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=dead > dead-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', 'dead'];\n const statusEmoji: Record<string, string> = {\n ready: 'β³',\n running: 'βΆοΈ',\n done: 'β
',\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 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"]}
|