3313f29954
- Make non-async helper functions async (triggerCoding, triggerWorkflow, writeVitalAndRebuild, writeEffectEvent, cleanup, trigger) - Fix bad await placement in object literals (await hash: -> hash: await) - Fix await in non-async arrow callbacks and await data: object property - Convert expect(async).toThrow() to rejects.toThrow() for async functions - Fix Promise.all pattern for async getObject calls in map - Remove stray await before return statements - Fix db.close() (sync bun:sqlite API) wrongly awaited - Auto-fix formatting issues via biome Made-with: Cursor
98 lines
3.0 KiB
TypeScript
98 lines
3.0 KiB
TypeScript
/**
|
|
* commands/status.ts — systemd workflow service + workflows.db summary
|
|
*/
|
|
|
|
import { Database } from 'bun:sqlite';
|
|
import { execSync } from 'node:child_process';
|
|
import type { Command } from 'commander';
|
|
import { loadConfig, resolveDir } from '../config.js';
|
|
import { workflowDataPaths } from '../store.js';
|
|
|
|
function systemctlStatus(unit: string): string {
|
|
try {
|
|
return execSync(`systemctl --user status ${unit} --no-pager 2>&1`, {
|
|
encoding: 'utf-8',
|
|
maxBuffer: 1024 * 1024,
|
|
});
|
|
} catch (err) {
|
|
const e = err as { stdout?: Buffer; stderr?: Buffer; message?: string };
|
|
return (
|
|
(e.stdout && e.stdout.toString()) ||
|
|
(e.stderr && e.stderr.toString()) ||
|
|
e.message ||
|
|
String(err)
|
|
);
|
|
}
|
|
}
|
|
|
|
export function registerStatusCommand(program: Command): void {
|
|
program
|
|
.command('status')
|
|
.description('Workflow daemon systemd status and store summary')
|
|
.action(() => {
|
|
const config = loadConfig(resolveDir(program.opts().dir));
|
|
const { eventsDbPath } = workflowDataPaths(config);
|
|
|
|
console.log('=== systemd (user) ===\n');
|
|
const units = ['pulse-workflow.service', 'pulse.service'];
|
|
for (const u of units) {
|
|
console.log(`--- ${u} ---`);
|
|
console.log(systemctlStatus(u));
|
|
console.log('');
|
|
}
|
|
|
|
console.log('=== workflows.db ===\n');
|
|
console.log(`Path: ${eventsDbPath}\n`);
|
|
|
|
try {
|
|
const db = new Database(eventsDbPath, { readonly: true });
|
|
try {
|
|
const row = db
|
|
.query('SELECT COUNT(*) AS c, MAX(occurred_at) AS m FROM events')
|
|
.get() as { c: number; m: number | null };
|
|
console.log(`Event count: ${row.c}`);
|
|
if (row.m != null) {
|
|
console.log(`Latest event: ${new Date(row.m).toISOString()}`);
|
|
}
|
|
} finally {
|
|
db.close();
|
|
}
|
|
} catch {
|
|
console.log('(database not readable or missing)');
|
|
}
|
|
|
|
console.log('\n=== active topics (recent keys) ===\n');
|
|
try {
|
|
const db = new Database(eventsDbPath, { readonly: true });
|
|
try {
|
|
const recent = db
|
|
.query(
|
|
`SELECT key, kind, occurred_at FROM events
|
|
WHERE key IS NOT NULL AND key != ''
|
|
ORDER BY occurred_at DESC, id DESC
|
|
LIMIT 30`,
|
|
)
|
|
.all() as { key: string; kind: string; occurred_at: number }[];
|
|
|
|
const seen = new Set<string>();
|
|
const lines: string[] = [];
|
|
for (const r of recent) {
|
|
if (seen.has(r.key)) continue;
|
|
seen.add(r.key);
|
|
lines.push(
|
|
` ${r.key} ${r.kind} ${new Date(r.occurred_at).toISOString()}`,
|
|
);
|
|
if (lines.length >= 10) break;
|
|
}
|
|
console.log(lines.length ? lines.join('\n') : ' (none)');
|
|
} finally {
|
|
db.close();
|
|
}
|
|
} catch {
|
|
console.log(' (could not query topics)');
|
|
}
|
|
|
|
console.log('');
|
|
});
|
|
}
|