@teamvibe/poller 0.1.50 → 0.1.53
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.
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { readdir, readFile, unlink } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { config } from './config.js';
|
|
4
|
+
import { logger } from './logger.js';
|
|
5
|
+
const OPERATION_TO_EVENT_TYPE = {
|
|
6
|
+
consolidation: 'maintenance.consolidation',
|
|
7
|
+
reflection: 'maintenance.reflection',
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Scan for JSON maintenance reports in the brain's reports/ directory
|
|
11
|
+
* and POST each to the /events endpoint. Fire-and-forget — never throws.
|
|
12
|
+
*/
|
|
13
|
+
export async function postMaintenanceReports(brainPath, channelId) {
|
|
14
|
+
if (!config.TEAMVIBE_API_URL || !config.TEAMVIBE_POLLER_TOKEN) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const reportsDir = join(brainPath, 'reports');
|
|
18
|
+
try {
|
|
19
|
+
const files = await readdir(reportsDir).catch(() => []);
|
|
20
|
+
const jsonFiles = files.filter((f) => f.endsWith('.json'));
|
|
21
|
+
if (jsonFiles.length === 0)
|
|
22
|
+
return;
|
|
23
|
+
logger.info(`[MaintenanceReporter] Found ${jsonFiles.length} JSON report(s) in ${reportsDir}`);
|
|
24
|
+
for (const file of jsonFiles) {
|
|
25
|
+
const filePath = join(reportsDir, file);
|
|
26
|
+
try {
|
|
27
|
+
const content = await readFile(filePath, 'utf-8');
|
|
28
|
+
const report = JSON.parse(content);
|
|
29
|
+
if (!report.operationType) {
|
|
30
|
+
logger.warn(`[MaintenanceReporter] Skipping ${file}: missing operationType`);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const eventType = OPERATION_TO_EVENT_TYPE[report.operationType];
|
|
34
|
+
if (!eventType) {
|
|
35
|
+
logger.warn(`[MaintenanceReporter] Skipping ${file}: unknown operationType "${report.operationType}"`);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const payload = {
|
|
39
|
+
eventType,
|
|
40
|
+
channelId,
|
|
41
|
+
metadata: report,
|
|
42
|
+
};
|
|
43
|
+
const response = await fetch(`${config.TEAMVIBE_API_URL}/events`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
Authorization: `Bearer ${config.TEAMVIBE_POLLER_TOKEN}`,
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify(payload),
|
|
50
|
+
});
|
|
51
|
+
if (response.ok) {
|
|
52
|
+
logger.info(`[MaintenanceReporter] Posted ${file} as ${eventType}`);
|
|
53
|
+
await unlink(filePath).catch(() => { });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const errorBody = await response.text().catch(() => 'unknown');
|
|
57
|
+
logger.warn(`[MaintenanceReporter] Failed to POST ${file}: ${response.status} ${errorBody}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
logger.warn(`[MaintenanceReporter] Error processing ${file}: ${err instanceof Error ? err.message : err}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
logger.warn(`[MaintenanceReporter] Error scanning reports: ${err instanceof Error ? err.message : err}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
package/dist/poller.js
CHANGED
|
@@ -5,6 +5,7 @@ import { spawnClaudeCode, isAtCapacity, getActiveProcessCount, killAllActiveProc
|
|
|
5
5
|
import { sendSlackError, addReaction, getUserInfo, startTypingIndicator } from './slack-client.js';
|
|
6
6
|
import { acquireSessionLock, releaseSessionLock, updateSessionId } from './session-store.js';
|
|
7
7
|
import { getBrainPath, ensureDirectories, ensureBaseBrain, pushBrainChanges } from './brain-manager.js';
|
|
8
|
+
import { postMaintenanceReports } from './maintenance-reporter.js';
|
|
8
9
|
import { initAuth, stopRefresh } from './auth-provider.js';
|
|
9
10
|
import { registerBrain, startHeartbeatLoop, stopHeartbeatLoop } from './heartbeat-manager.js';
|
|
10
11
|
// Track active message processing
|
|
@@ -183,6 +184,8 @@ async function processMessage(received) {
|
|
|
183
184
|
if (queueMessage.teamvibe.brain?.brainId) {
|
|
184
185
|
await pushBrainChanges(kbPath, queueMessage.teamvibe.brain.brainId, queueMessage.teamvibe.workspaceId);
|
|
185
186
|
}
|
|
187
|
+
// Post any pending maintenance reports (fire-and-forget)
|
|
188
|
+
await postMaintenanceReports(kbPath, queueMessage.teamvibe.channelId);
|
|
186
189
|
if (lockToken) {
|
|
187
190
|
const lastMessageTs = queueMessage.response_context.slack?.message_ts;
|
|
188
191
|
await releaseSessionLock(threadId, lockToken, 'idle', lastMessageTs);
|