@teamvibe/poller 0.1.28 → 0.1.29

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.
@@ -11,3 +11,4 @@ export declare function spawnClaudeCode(msg: TeamVibeQueueMessage, sessionLog: S
11
11
  }>;
12
12
  export declare function getActiveProcessCount(): number;
13
13
  export declare function isAtCapacity(): boolean;
14
+ export declare function killAllActiveProcesses(): Promise<void>;
@@ -298,3 +298,26 @@ export function getActiveProcessCount() {
298
298
  export function isAtCapacity() {
299
299
  return activeProcesses.size >= config.MAX_CONCURRENT_SESSIONS;
300
300
  }
301
+ export function killAllActiveProcesses() {
302
+ if (activeProcesses.size === 0)
303
+ return Promise.resolve();
304
+ logger.info(`Killing ${activeProcesses.size} active Claude process(es)...`);
305
+ const killPromises = Array.from(activeProcesses.entries()).map(([id, proc]) => new Promise((resolve) => {
306
+ if (proc.exitCode !== null) {
307
+ resolve();
308
+ return;
309
+ }
310
+ const forceKillTimer = setTimeout(() => {
311
+ logger.warn(`Process ${id} did not exit after SIGTERM, sending SIGKILL`);
312
+ proc.kill('SIGKILL');
313
+ }, 5000);
314
+ proc.once('close', () => {
315
+ clearTimeout(forceKillTimer);
316
+ resolve();
317
+ });
318
+ proc.kill('SIGTERM');
319
+ }));
320
+ return Promise.all(killPromises).then(() => {
321
+ logger.info('All active processes terminated');
322
+ });
323
+ }
package/dist/poller.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { config } from './config.js';
2
2
  import { logger } from './logger.js';
3
3
  import { pollMessages, deleteMessage, extendVisibility, } from './sqs-poller.js';
4
- import { spawnClaudeCode, isAtCapacity, getActiveProcessCount } from './claude-spawner.js';
4
+ import { spawnClaudeCode, isAtCapacity, getActiveProcessCount, killAllActiveProcesses } from './claude-spawner.js';
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';
@@ -191,7 +191,7 @@ async function processMessage(received) {
191
191
  }
192
192
  async function pollLoop() {
193
193
  logger.info('Poll loop started');
194
- while (true) {
194
+ while (!shuttingDown) {
195
195
  try {
196
196
  if (isAtCapacity()) {
197
197
  logger.debug(`At capacity (${getActiveProcessCount()}/${config.MAX_CONCURRENT_SESSIONS}), waiting...`);
@@ -229,6 +229,16 @@ async function shutdown(signal) {
229
229
  shuttingDown = true;
230
230
  logger.info(`${signal} received, shutting down gracefully...`);
231
231
  stopRefresh();
232
+ // Unblock all messages waiting on thread completion so they can exit
233
+ for (const [threadId, signals] of threadCompletionSignals.entries()) {
234
+ logger.info(`Unblocking ${signals.length} waiting message(s) on thread ${threadId}`);
235
+ signals.forEach((resolve) => resolve());
236
+ }
237
+ threadCompletionSignals.clear();
238
+ waitingCountByThread.clear();
239
+ // Kill all active Claude child processes
240
+ await killAllActiveProcesses();
241
+ // Wait for processingMessages to drain (with timeout)
232
242
  const shutdownTimeout = 30000;
233
243
  const startTime = Date.now();
234
244
  while (processingMessages.size > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamvibe/poller",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "bin": {