@tyhld/conductor 0.12.81 → 0.12.83

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.
package/README.md CHANGED
@@ -106,6 +106,19 @@ APPLY=1 bash scripts/update-allowed-domains.sh # 実際に配る
106
106
  これを流さないと職人の動きは変わりません(`update-guard.sh ①-d`。同一なら何もしません)。
107
107
  - **`setup.sh` からは呼びません**。顧客機の初期設置に大きなダウンロードを足すと設置そのものが止まるためです(ADR-010)。
108
108
 
109
+ ### 職人が始まるのに要る物(先頭の確かめ・0.12.82〜)
110
+
111
+ 1行インストーラ(`sales-template/install.sh`)と `scripts/update-guard.sh` は、**設定を配る前に**次を確かめ、足りなければ止まって「人が打つ1行」を出します。★台本は自分で `sudo` を打たず、何も入れません。
112
+
113
+ - **職人のソフト(Claude Code)が 2.1.282 以上**(えふさん確定 2026-10-02)。中継と同じ場所を同じ順で探し、いちばん新しい版で判定します。上げ方は入れ方で違います。
114
+ - ネイティブ導入(`~/.local/bin/claude`)… `~/.local/bin/claude update`
115
+ - npm 導入(nvm の bin)… `npm install -g @anthropic-ai/claude-code@latest`
116
+ - **囲いの部品(Linux・WSL)**: `bwrap`(bubblewrap)と `socat`。無いと職人が安全のため起動を断ります(設定 `sandbox.failIfUnavailable`)。
117
+ - `sudo apt-get install -y bubblewrap socat`
118
+ - sudo のパスワードが分からないとき(WSL・Windows の PowerShell で): `wsl.exe -u root apt-get install -y bubblewrap socat`
119
+ - Mac は囲いが OS に組み込みなので求めません。分からない機は止めずに「確かめられませんでした」と出します。
120
+ - 確認だけ(`bash scripts/update-guard.sh`)のときは、職人が管制へ知らせる口(ai-kana の MCP)がつながっているかも1行で出します(止めません)。
121
+
109
122
  ---
110
123
 
111
124
  ## 使い方
package/dist/cli.js CHANGED
@@ -488,12 +488,15 @@ function printUsage() {
488
488
  ' conductor ear この機に1本の「耳」(合図の受け取り役)',
489
489
  ' conductor build-runner この機に1本の「司令塔」(試作の段取り役)',
490
490
  ' conductor setup <現場名> [フォルダ] [--profile <default|proto>] 采配くんをこのPCに設置する(初回の1回だけ)',
491
- ' conductor view <現場名> [--detail] その現場の今を見るだけの画面(kc の実体)',
491
+ ' conductor view <現場名> [--once] [--detail] その現場の今を見るだけの画面(kc の実体)',
492
492
  '',
493
493
  '見るだけの画面(conductor view = kc 現場名):',
494
494
  ' いま職人が何をしているかを、やさしい日本語で見るだけの画面です。',
495
+ ' ★既定は「開いたまま流れ続ける」形です。職人の動きが出るたびに下へ流れます。',
496
+ ' 閉じるときは Ctrl+C(★抜けても職人・見守り役には何も起きません)。',
495
497
  ' ★職人(Claude Code)・tmux・relay を起動しません。入力も受け付けません(抜けるだけ)。',
496
- ' --detail を付けると、職人の生の記録(秘密は隠したもの)も続けて出します。',
498
+ ' --once これまでどおり「いまの様子を1回だけ」出して終わります。',
499
+ ' --detail 職人の生の記録(秘密は隠したもの)も続けて出します。',
497
500
  '',
498
501
  '設置の型(--profile。既定は default=今までどおり):',
499
502
  ' default ふつうの現場。従来と同じ設定を置きます。',
@@ -573,7 +576,8 @@ async function main() {
573
576
  break;
574
577
  case 'view':
575
578
  // ★見るだけの画面。職人も tmux も relay も起動せず、入力も受け付けない。
576
- process.exit(runViewCommand(process.argv.slice(3)));
579
+ // 既定は「開いたまま流れ続ける」ので、Ctrl+C まで戻らない(--once は1回で戻る)。
580
+ process.exit(await runViewCommand(process.argv.slice(3)));
577
581
  break;
578
582
  case 'version':
579
583
  case '-v':
package/dist/headless.js CHANGED
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import { execFileSync, spawn } from 'node:child_process';
18
18
  import { createInterface } from 'node:readline';
19
- import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
19
+ import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
20
20
  import path from 'node:path';
21
21
  import os from 'node:os';
22
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, recoverStuckProcessedReports, reconnectDelayMs, selectOrphanToRestore, sendHeartbeat, } from './relay.js';
@@ -282,13 +282,13 @@ export function validateActiveJobOwnership(rec, cfg) {
282
282
  /**
283
283
  * 必要な最低の版。これより古い Claude Code では職人を起動しない。
284
284
  *
285
- * 根拠: Claude Code 2.1.x 系で画面なし方式(-p, --output-format stream-json)と
286
- * 通信の許可 "*"(allowedTools: "*")が動くことが確かめられたのは太田さんの PC の
287
- * 2.1.286(便 bcce7be9)。2.1.86 では "*" が効かない疑い(便 a9754912、メインPC)。
288
- * 安全側に倒して 2.1.200 を最低とする(200 未満は "*" 対応が不明確な版)。
289
- * ★確かめられた版は 2.1.286 だけなので、200 は推定の境界。
285
+ * えふさん確定(2026-10-02・判断1): 2.1.282。
286
+ * 根拠: 公式文書(code.claude.com/docs/en/managed-settings)が、管理設定の安全側の
287
+ * 決まりに Claude Code v2.1.282 以降を求めている。通信の許可 "*" が効くと実測できたのは
288
+ * 2.1.286(便 bcce7be9)。2.1.86 では効かない疑い(便 a9754912、メインPC)。
289
+ * ★scripts/preflight.sh の CLAUDE_MIN_VERSION も同じ値にする(試験で一致を固定)。
290
290
  */
291
- export const CLAUDE_MIN_VERSION = '2.1.200';
291
+ export const CLAUDE_MIN_VERSION = '2.1.282';
292
292
  /** claude --version の出力から版番号を取り出す。取れなければ null。 */
293
293
  export function parseClaudeVersion(output) {
294
294
  // "2.1.286 (Claude Code)" → "2.1.286"
@@ -320,8 +320,42 @@ export function getClaudeVersion(binPath) {
320
320
  return null;
321
321
  }
322
322
  }
323
+ /**
324
+ * 選ばれた claude の入れ方に合う「更新の1行」を返す(そのままコピペで動く形)。
325
+ *
326
+ * 入れ方は2通り在り、上げ方が違う(実測 2026-10-02 ノートPC: ネイティブ導入は
327
+ * npm の1行では上がらず、claude update で 2.1.70 → 2.1.287 になった)。
328
+ * - ネイティブ導入(~/.local/bin/claude → ~/.local/share/claude/versions/…、
329
+ * または ~/.claude/local/claude)… その claude で `update`
330
+ * - npm 導入(実体が node_modules/@anthropic-ai/claude-code の下、または nvm の bin)
331
+ * … その bin の npm で `install -g @anthropic-ai/claude-code@latest`
332
+ * - 見分けられない … 両方を書く
333
+ */
334
+ export function claudeUpdateHint(bin) {
335
+ let real = bin;
336
+ try {
337
+ real = realpathSync(bin);
338
+ }
339
+ catch {
340
+ // 実体を辿れなくても、置き場の名前だけで見分ける
341
+ }
342
+ const h = os.homedir();
343
+ const npmLine = (dir) => `PATH="${dir}:$PATH" npm install -g @anthropic-ai/claude-code@latest`;
344
+ const isNpm = (p) => p.includes('/node_modules/@anthropic-ai/claude-code/') || p.includes('/.nvm/versions/node/');
345
+ const isNative = (p) => p.startsWith(path.join(h, '.local', 'share', 'claude') + '/') ||
346
+ p.startsWith(path.join(h, '.claude', 'local') + '/');
347
+ if (isNpm(real) || isNpm(bin)) {
348
+ return `npm 導入の更新: ${npmLine(path.dirname(bin))}`;
349
+ }
350
+ if (isNative(real) || isNative(bin)) {
351
+ return `ネイティブ導入の更新: "${bin}" update`;
352
+ }
353
+ return `入れ方を見分けられませんでした。ネイティブ導入なら "${bin}" update / npm 導入なら npm install -g @anthropic-ai/claude-code@latest`;
354
+ }
355
+ /** ホームの外に在る、claude の既知の置き場(既定の候補)。 */
356
+ export const CLAUDE_SYSTEM_BIN_DIRS = ['/usr/local/bin', '/usr/bin'];
323
357
  /** claude の実行ファイルの既知の候補を返す(新しい node の版から順)。 */
324
- function claudeBinCandidates() {
358
+ function claudeBinCandidates(systemDirs) {
325
359
  const h = os.homedir();
326
360
  const candidates = [];
327
361
  // nvm の各 node 版の bin(新しい版から)
@@ -350,7 +384,7 @@ function claudeBinCandidates() {
350
384
  }
351
385
  }
352
386
  // その他の既知の場所
353
- candidates.push(path.join(h, '.local', 'bin', 'claude'), path.join(h, '.claude', 'local', 'claude'), '/usr/local/bin/claude', '/usr/bin/claude');
387
+ candidates.push(path.join(h, '.local', 'bin', 'claude'), path.join(h, '.claude', 'local', 'claude'), ...systemDirs.map(d => path.join(d, 'claude')));
354
388
  return candidates;
355
389
  }
356
390
  /**
@@ -360,15 +394,18 @@ function claudeBinCandidates() {
360
394
  * 1. 環境変数 CONDUCTOR_CLAUDE_BIN(テスト用の差し替え口)→ 指定があればそれだけ
361
395
  * 2. 候補のうち実在する物の版を確かめ、いちばん新しい版を使う
362
396
  * 3. 版を確かめられない物は後回し(版が分かる物が1つもないときだけ使う)
397
+ *
398
+ * systemDirs はホームの外の置き場(既定 /usr/local/bin・/usr/bin)。試験は機体に
399
+ * 依らないよう、ここを作り物のフォルダに差し替える。
363
400
  */
364
- export function resolveClaudeBin() {
401
+ export function resolveClaudeBin(systemDirs = CLAUDE_SYSTEM_BIN_DIRS) {
365
402
  const override = process.env.CONDUCTOR_CLAUDE_BIN;
366
403
  if (override) {
367
404
  if (!existsSync(override))
368
405
  return null;
369
406
  return { bin: override, version: getClaudeVersion(override) };
370
407
  }
371
- const existing = claudeBinCandidates().filter(c => existsSync(c));
408
+ const existing = claudeBinCandidates(systemDirs).filter(c => existsSync(c));
372
409
  if (existing.length === 0)
373
410
  return null;
374
411
  // 版が分かる候補と分からない候補を分ける
@@ -792,6 +829,8 @@ function closeLog(ws) {
792
829
  */
793
830
  export let lastSpawnError = '';
794
831
  export function spawnClaudeProcess(cfg, cmd, sessionId, attempt, isResume) {
832
+ // 前の失敗の理由を持ち越さない(理由の無い失敗に古い理由が付かないように)
833
+ lastSpawnError = '';
795
834
  const resolved = resolveClaudeBin();
796
835
  if (resolved === null) {
797
836
  lastSpawnError = 'claude が見つかりません。探した場所: nvm の各版の bin, ~/.local/bin, ~/.claude/local, /usr/local/bin, /usr/bin';
@@ -801,7 +840,8 @@ export function spawnClaudeProcess(cfg, cmd, sessionId, attempt, isResume) {
801
840
  const { bin: claudeBin, version: claudeVersion } = resolved;
802
841
  // ── 最低版チェック ──
803
842
  if (claudeVersion && compareVersions(claudeVersion, CLAUDE_MIN_VERSION) < 0) {
804
- lastSpawnError = `Claude Code が古すぎます(${claudeBin} = ${claudeVersion}、最低 ${CLAUDE_MIN_VERSION})。npm install -g @anthropic-ai/claude-code で更新してください`;
843
+ // ★候補のうちいちばん新しい物でも最低版より古い=全部が古いときだけここに来る
844
+ lastSpawnError = `Claude Code が古すぎるため仕事を始めません(${claudeBin} = ${claudeVersion}、必要な版 ${CLAUDE_MIN_VERSION} 以上)。${claudeUpdateHint(claudeBin)}`;
805
845
  log(`ERROR ${lastSpawnError}`);
806
846
  return null;
807
847
  }
@@ -851,7 +891,8 @@ export function spawnClaudeProcess(cfg, cmd, sessionId, attempt, isResume) {
851
891
  });
852
892
  }
853
893
  catch (err) {
854
- log(`ERROR claude の起動に失敗しました (${claudeBin}): ${err instanceof Error ? err.message : String(err)}`);
894
+ lastSpawnError = `claude の起動に失敗しました (${claudeBin}): ${err instanceof Error ? err.message : String(err)}`;
895
+ log(`ERROR ${lastSpawnError}`);
855
896
  return null;
856
897
  }
857
898
  const logs = openRunLogs(cmd.id, attempt);