@tyhld/conductor 0.12.69 → 0.12.70

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.
Files changed (2) hide show
  1. package/dist/headless.js +24 -4
  2. package/package.json +1 -1
package/dist/headless.js CHANGED
@@ -19,7 +19,7 @@ import { createInterface } from 'node:readline';
19
19
  import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from 'node:fs';
20
20
  import path from 'node:path';
21
21
  import os from 'node:os';
22
- import { RELAY_DELIVER_MAX, RELAY_FETCH_TIMEOUT_MS, RELAY_HEARTBEAT_INTERVAL_MS, RELAY_PENDING_TIMEOUT_MS, RELAY_RESTORE_ORPHANS_ENABLED, buildAnchor, buildSendText, canDeliver, completeWithReport, createRelayState, deliveryReport, fetchCommand, fetchCommandStatus, fetchControl, fetchOrphans, isTerminalCommandStatus, kindOf, markDelivered, noteCentralDown, noteCentralUp, noteReadiness, patchStatus, reconnectDelayMs, selectOrphanToRestore, sendHeartbeat, } from './relay.js';
22
+ import { CENTRAL_BUSY_HOLD_MAX_MS, RELAY_DELIVER_MAX, RELAY_FETCH_TIMEOUT_MS, RELAY_HEARTBEAT_INTERVAL_MS, RELAY_RESTORE_ORPHANS_ENABLED, buildAnchor, buildSendText, canDeliver, completeWithReport, createRelayState, deliveryReport, fetchCommand, fetchCommandStatus, fetchControl, fetchOrphans, isTerminalCommandStatus, kindOf, markDelivered, noteCentralDown, noteCentralUp, noteReadiness, patchStatus, reconnectDelayMs, selectOrphanToRestore, sendHeartbeat, } from './relay.js';
23
23
  const log = (msg) => {
24
24
  console.log(`[headless] ${msg}`);
25
25
  };
@@ -471,6 +471,7 @@ export function spawnClaudeProcess(cfg, cmd, sessionId, attempt, isResume) {
471
471
  exitCode: null,
472
472
  exitSignal: null,
473
473
  stopped: false,
474
+ timedOut: false,
474
475
  eventBuffer: [],
475
476
  lastFlushAt: Date.now(),
476
477
  lastStopCheckAt: Date.now(),
@@ -626,6 +627,23 @@ async function checkRunningJob(cfg, state) {
626
627
  if (job.completed) {
627
628
  await flushEvents(cfg, job);
628
629
  clearActiveJob(cfg.site);
630
+ if (job.timedOut) {
631
+ // 上限時間で強制停止した → 管制へ理由つきで failed を記録する
632
+ const elapsed = Math.round((Date.now() - job.startedAt) / 1000);
633
+ const reason = `上限時間(${Math.round(CENTRAL_BUSY_HOLD_MAX_MS / 1000)}s)で強制停止 (実行${elapsed}s, exit=${job.exitCode}, signal=${job.exitSignal})`;
634
+ await sendCommandEvents(cfg, job.commandId, [{
635
+ type: 'error',
636
+ text: reason.slice(0, EVENT_TEXT_MAX),
637
+ at: new Date().toISOString(),
638
+ attempt: job.attempt,
639
+ }]);
640
+ await patchStatus(cfg, job.commandId, 'failed', reason.slice(0, EVENT_TEXT_MAX));
641
+ rs.abandoned.add(job.commandId);
642
+ state.job = null;
643
+ rs.lastHeartbeatAt = null;
644
+ log(`WARN site=${cfg.site} id=${job.commandId} ${reason}`);
645
+ return;
646
+ }
629
647
  if (job.stopped) {
630
648
  // stop_requested で止めた
631
649
  await sendCommandEvents(cfg, job.commandId, [{
@@ -719,10 +737,12 @@ async function checkRunningJob(cfg, state) {
719
737
  return;
720
738
  }
721
739
  }
722
- // タイムアウト
723
- if (now - job.startedAt > RELAY_PENDING_TIMEOUT_MS) {
740
+ // タイムアウト — 中央の上限(3時間)で強制停止する。
741
+ // ★RELAY_PENDING_TIMEOUT_MS(15分)は tmux の pending 解除用。画面なし方式では
742
+ // 作業中の職人そのものを殺すので、中央と同じ CENTRAL_BUSY_HOLD_MAX_MS を使う。
743
+ if (now - job.startedAt > CENTRAL_BUSY_HOLD_MAX_MS) {
724
744
  log(`WARN site=${cfg.site} id=${job.commandId} タイムアウト(${Math.round((now - job.startedAt) / 1000)}s) → SIGTERM`);
725
- job.stopped = true;
745
+ job.timedOut = true;
726
746
  await stopJob(job);
727
747
  return;
728
748
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tyhld/conductor",
3
- "version": "0.12.69",
3
+ "version": "0.12.70",
4
4
  "description": "采配くん管制の見守りアプリ(conductor-agent)。各PCで常駐し、中央(devlog-tracker)へ定期的に生存報告(heartbeat)を送る常駐CLI。",
5
5
  "type": "module",
6
6
  "bin": {