@tyhld/conductor 0.12.63 → 0.12.64

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 +53 -2
  2. package/package.json +1 -1
package/dist/headless.js CHANGED
@@ -268,8 +268,51 @@ export function streamEventToCommandEvent(parsed, attempt) {
268
268
  }
269
269
  return null;
270
270
  }
271
- /** 子プロセスの stderr から認証エラーを検出する。 */
271
+ /**
272
+ * stream-json の1行から認証エラーを検出する。
273
+ *
274
+ * 実物(2026-10-01 メインPC Fshima・ログイン切れ):
275
+ * assistant 行: error="authentication_failed", text="Not logged in · Please run /login"
276
+ * result 行: subtype="success", is_error=true, result="Not logged in · Please run /login"
277
+ * stderr: 空。exit code: 1。
278
+ *
279
+ * ★stderr には何も出ない。stdout の stream-json でだけ分かる。
280
+ */
281
+ export function detectStreamAuthError(parsed) {
282
+ // assistant 行の error フィールド(最も確実な判定)
283
+ if (parsed.type === 'assistant') {
284
+ const msg = parsed.message;
285
+ if (msg) {
286
+ // assistant 直下にも error がある場合がある(実物で確認)
287
+ }
288
+ if (parsed.error === 'authentication_failed')
289
+ return true;
290
+ }
291
+ // assistant.message.content[].text or parsed.error on assistant envelope
292
+ if (parsed.type === 'assistant' && typeof parsed.message === 'object' && parsed.message !== null) {
293
+ const msg = parsed.message;
294
+ if (msg.error === 'authentication_failed')
295
+ return true;
296
+ }
297
+ // result 行: is_error=true かつ result テキストにログイン切れの文言
298
+ if (parsed.type === 'result' && parsed.is_error === true) {
299
+ const result = String(parsed.result ?? '').toLowerCase();
300
+ if (result.includes('not logged in') ||
301
+ result.includes('please run /login') ||
302
+ result.includes('login required') ||
303
+ result.includes('session expired') ||
304
+ result.includes('token expired')) {
305
+ return true;
306
+ }
307
+ }
308
+ return false;
309
+ }
310
+ /** 子プロセスの stderr と stream-json の両方から認証エラーを検出する。 */
272
311
  export function isAuthError(job) {
312
+ // ★stream-json(stdout)で検出済み
313
+ if (job.authErrorDetected)
314
+ return true;
315
+ // stderr からも検出(従来の判定。API キーの認証エラーなど)
273
316
  const s = job.stderrBuf.toLowerCase();
274
317
  return (s.includes('401') ||
275
318
  s.includes('unauthorized') ||
@@ -387,6 +430,7 @@ export function spawnClaudeProcess(cfg, cmd, sessionId, attempt, isResume) {
387
430
  lastFlushAt: Date.now(),
388
431
  lastStopCheckAt: Date.now(),
389
432
  stderrBuf: '',
433
+ authErrorDetected: false,
390
434
  stdoutLog: logs?.stdoutLog ?? null,
391
435
  stderrLog: logs?.stderrLog ?? null,
392
436
  durationMs: 0,
@@ -421,6 +465,10 @@ export function spawnClaudeProcess(cfg, cmd, sessionId, attempt, isResume) {
421
465
  job.stdoutLog?.write(line + '\n');
422
466
  try {
423
467
  const parsed = JSON.parse(line);
468
+ // ★stream-json から認証エラーを検出(stderr に出ない実物に対応)
469
+ if (!job.authErrorDetected && detectStreamAuthError(parsed)) {
470
+ job.authErrorDetected = true;
471
+ }
424
472
  const event = streamEventToCommandEvent(parsed, attempt);
425
473
  if (event)
426
474
  job.eventBuffer.push(event);
@@ -548,7 +596,10 @@ async function checkRunningJob(cfg, state) {
548
596
  }
549
597
  if (isAuthError(job)) {
550
598
  // 認証エラー → 再試行しない
551
- const authMsg = maskSecrets(`認証エラー (exit=${job.exitCode}): ${job.stderrBuf.slice(0, 300)}`).slice(0, EVENT_TEXT_MAX);
599
+ const detail = job.authErrorDetected
600
+ ? 'ログイン切れ(stream-json で検出)'
601
+ : job.stderrBuf.slice(0, 300);
602
+ const authMsg = maskSecrets(`認証エラー (exit=${job.exitCode}): ${detail}`).slice(0, EVENT_TEXT_MAX);
552
603
  await sendCommandEvents(cfg, job.commandId, [{
553
604
  type: 'auth_error',
554
605
  text: authMsg,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tyhld/conductor",
3
- "version": "0.12.63",
3
+ "version": "0.12.64",
4
4
  "description": "采配くん管制の見守りアプリ(conductor-agent)。各PCで常駐し、中央(devlog-tracker)へ定期的に生存報告(heartbeat)を送る常駐CLI。",
5
5
  "type": "module",
6
6
  "bin": {