@tyhld/conductor 0.12.81 → 0.12.82
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 +13 -0
- package/dist/headless.js +54 -13
- package/package.json +1 -1
- package/sales-template/install.sh +187 -0
- package/sales-template/setup.sh +3 -1
- package/scripts/hooks/test_policy.py +11 -0
- package/scripts/update-guard.sh +211 -0
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/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
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
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.
|
|
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'),
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/package.json
CHANGED
|
@@ -387,6 +387,172 @@ run() {
|
|
|
387
387
|
"$@"
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
+
# >>> preflight >>> ★この印の間が正本。install.sh と update-guard.sh で1文字も違わないことをテストが固定する。
|
|
391
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
392
|
+
# 先頭の確かめ(職人が始まるのに要る物・便 ff8f4be9)
|
|
393
|
+
#
|
|
394
|
+
# 【なぜ要るか(実機 2026-10-02・ノートPC)】
|
|
395
|
+
# 便 7901973d が3回続けて起動に失敗した。囲いの部品 bubblewrap・socat が入っておらず、
|
|
396
|
+
# 設定 sandbox.failIfUnavailable のため職人が起動を断っていた。更新までは気づかれずに動き、
|
|
397
|
+
# 更新の後に職人が始まらなくなった。また、最低版(えふさん確定 2.1.282)より古い
|
|
398
|
+
# Claude Code では中継が職人を始めない。=設定を配る前にここで気づき、人が打つ1行を出す。
|
|
399
|
+
#
|
|
400
|
+
# 【決め】
|
|
401
|
+
# ・確かめるだけ。★台本が自分で sudo を打たない・勝手に入れない(人の窓)。
|
|
402
|
+
# ・Claude Code は中継(src/headless.ts の resolveClaudeBin)と同じ場所を同じ順で探し、
|
|
403
|
+
# いちばん新しい版で判定する(CONDUCTOR_CLAUDE_BIN の指定があればそれだけ)。
|
|
404
|
+
# ・囲いの部品は、その機の仕組みに要る物だけを見る。
|
|
405
|
+
# Linux(WSL を含む)… bwrap と socat
|
|
406
|
+
# Mac … OS に組み込み(追加の部品は要らない)=何も求めない
|
|
407
|
+
# それ以外 … 止めずに「確かめられませんでした」と出す
|
|
408
|
+
# ・出す1行は、そのままコピペで動く形(山かっこの穴埋めなし)。
|
|
409
|
+
#
|
|
410
|
+
# 【使い方】preflight_check <claude が無いときの扱い: need|later>
|
|
411
|
+
# 結果: PREFLIGHT_NG(足りない物の説明と1行。空なら通過)/PREFLIGHT_INFO(止めない注意)
|
|
412
|
+
# /PREFLIGHT_CLAUDE_BIN(中継が使うはずの claude・見つからなければ空)
|
|
413
|
+
# ★later … 後の段で Claude Code を入れる台本(1行インストーラ)用。無いことだけは止めない
|
|
414
|
+
# (古いときは止める)。
|
|
415
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
416
|
+
# ★src/headless.ts の CLAUDE_MIN_VERSION と同じ値(テストで一致を固定)。
|
|
417
|
+
PREFLIGHT_CLAUDE_MIN='2.1.282'
|
|
418
|
+
|
|
419
|
+
# a が b より古いか(数字3つだけを見る。読めない字は 0=古い扱い)。
|
|
420
|
+
preflight_ver_lt() {
|
|
421
|
+
local a="${1%%-*}" b="${2%%-*}" i x y
|
|
422
|
+
local -a A B
|
|
423
|
+
IFS='.' read -r -a A <<< "$a"
|
|
424
|
+
IFS='.' read -r -a B <<< "$b"
|
|
425
|
+
for i in 0 1 2; do
|
|
426
|
+
x="${A[i]:-0}"; y="${B[i]:-0}"
|
|
427
|
+
[[ "$x" =~ ^[0-9]+$ ]] || x=0
|
|
428
|
+
[[ "$y" =~ ^[0-9]+$ ]] || y=0
|
|
429
|
+
if (( x < y )); then return 0; fi
|
|
430
|
+
if (( x > y )); then return 1; fi
|
|
431
|
+
done
|
|
432
|
+
return 1
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
# 中継と同じ候補を同じ順で出す(1行に1つ)。
|
|
436
|
+
preflight_claude_candidates() {
|
|
437
|
+
if [[ -n "${CONDUCTOR_CLAUDE_BIN:-}" ]]; then
|
|
438
|
+
printf '%s\n' "$CONDUCTOR_CLAUDE_BIN"
|
|
439
|
+
return 0
|
|
440
|
+
fi
|
|
441
|
+
local d
|
|
442
|
+
local -a nv=()
|
|
443
|
+
for d in "$HOME/.nvm/versions/node"/v*; do
|
|
444
|
+
[[ -d "$d" ]] && nv+=("${d##*/}")
|
|
445
|
+
done
|
|
446
|
+
if (( ${#nv[@]} > 0 )); then
|
|
447
|
+
# 新しい node の版から(v24.21.0 → 24 21 0 で降順)
|
|
448
|
+
while IFS= read -r d; do
|
|
449
|
+
printf '%s\n' "$HOME/.nvm/versions/node/$d/bin/claude"
|
|
450
|
+
done < <(printf '%s\n' "${nv[@]}" | sort -t. -k1.2,1nr -k2,2nr -k3,3nr)
|
|
451
|
+
fi
|
|
452
|
+
printf '%s\n' "$HOME/.local/bin/claude" "$HOME/.claude/local/claude" /usr/local/bin/claude /usr/bin/claude
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
# その claude の入れ方に合う更新の1行(src/headless.ts の claudeUpdateHint と同じ見分け方)。
|
|
456
|
+
preflight_claude_update_line() {
|
|
457
|
+
local bin="$1" real="$1" t i
|
|
458
|
+
# 実体を辿る(★readlink -f は使わない=無いパスでも文字列を返すため。1段ずつ辿る)
|
|
459
|
+
for i in 1 2 3 4 5 6 7 8; do
|
|
460
|
+
[[ -L "$real" ]] || break
|
|
461
|
+
t="$(readlink "$real" 2>/dev/null || true)"
|
|
462
|
+
[[ -n "$t" ]] || break
|
|
463
|
+
case "$t" in
|
|
464
|
+
/*) real="$t" ;;
|
|
465
|
+
*) real="$(dirname "$real")/$t" ;;
|
|
466
|
+
esac
|
|
467
|
+
done
|
|
468
|
+
case "$real$bin" in
|
|
469
|
+
*/node_modules/@anthropic-ai/claude-code/*|*/.nvm/versions/node/*)
|
|
470
|
+
printf ' PATH="%s:$PATH" npm install -g @anthropic-ai/claude-code@latest\n' "$(dirname "$bin")"
|
|
471
|
+
return 0 ;;
|
|
472
|
+
esac
|
|
473
|
+
case "$real" in
|
|
474
|
+
"$HOME"/.local/share/claude/*|"$HOME"/.claude/local/*)
|
|
475
|
+
printf ' "%s" update\n' "$bin"
|
|
476
|
+
return 0 ;;
|
|
477
|
+
esac
|
|
478
|
+
printf ' (入れ方を見分けられませんでした。どちらか合う方を打ってください)\n'
|
|
479
|
+
printf ' ネイティブ導入なら: "%s" update\n' "$bin"
|
|
480
|
+
printf ' npm 導入なら : npm install -g @anthropic-ai/claude-code@latest\n'
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
preflight_check() {
|
|
484
|
+
local claude_mode="${1:-need}" c v best='' best_v='' unknown='' os missing=''
|
|
485
|
+
PREFLIGHT_NG=''
|
|
486
|
+
PREFLIGHT_INFO=''
|
|
487
|
+
PREFLIGHT_CLAUDE_BIN=''
|
|
488
|
+
|
|
489
|
+
# ── ① Claude Code が在り、最低版以上か ──
|
|
490
|
+
while IFS= read -r c; do
|
|
491
|
+
[[ -n "$c" && -x "$c" ]] || continue
|
|
492
|
+
v="$("$c" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n 1 || true)"
|
|
493
|
+
if [[ -z "$v" ]]; then
|
|
494
|
+
[[ -n "$unknown" ]] || unknown="$c"
|
|
495
|
+
continue
|
|
496
|
+
fi
|
|
497
|
+
if [[ -z "$best_v" ]] || preflight_ver_lt "$best_v" "$v"; then
|
|
498
|
+
best="$c"; best_v="$v"
|
|
499
|
+
fi
|
|
500
|
+
done < <(preflight_claude_candidates)
|
|
501
|
+
|
|
502
|
+
# shellcheck disable=SC2034 # update-guard.sh の ⓪-b が使う(install.sh では使わない)
|
|
503
|
+
PREFLIGHT_CLAUDE_BIN="${best:-$unknown}"
|
|
504
|
+
if [[ -n "$best_v" ]]; then
|
|
505
|
+
if preflight_ver_lt "$best_v" "$PREFLIGHT_CLAUDE_MIN"; then
|
|
506
|
+
PREFLIGHT_NG+="■ 職人のソフト(Claude Code)が古いです(${best} = ${best_v}、必要な版 ${PREFLIGHT_CLAUDE_MIN} 以上)。"$'\n'
|
|
507
|
+
PREFLIGHT_NG+=" この機の入れ方に合う1行:"$'\n'
|
|
508
|
+
PREFLIGHT_NG+="$(preflight_claude_update_line "$best")"$'\n'
|
|
509
|
+
else
|
|
510
|
+
PREFLIGHT_INFO+="✓ 職人のソフト(Claude Code): ${best} = ${best_v}(必要な版 ${PREFLIGHT_CLAUDE_MIN} 以上)"$'\n'
|
|
511
|
+
fi
|
|
512
|
+
elif [[ -n "$unknown" ]]; then
|
|
513
|
+
# ★中継も版の読めない claude は止めずに使う。ここでも止めない(作りをそろえる)。
|
|
514
|
+
PREFLIGHT_INFO+="⚠ 職人のソフト(Claude Code)の版を確かめられませんでした(${unknown})。"$'\n'
|
|
515
|
+
elif [[ "$claude_mode" == "need" ]]; then
|
|
516
|
+
PREFLIGHT_NG+="■ 職人のソフト(Claude Code)が見つかりません。"$'\n'
|
|
517
|
+
PREFLIGHT_NG+=" 入れる1行:"$'\n'
|
|
518
|
+
PREFLIGHT_NG+=" npm install -g @anthropic-ai/claude-code@latest"$'\n'
|
|
519
|
+
fi
|
|
520
|
+
|
|
521
|
+
# ── ② 囲いの部品(その機の仕組みに要る物だけ) ──
|
|
522
|
+
os="$(uname -s 2>/dev/null || echo unknown)"
|
|
523
|
+
case "$os" in
|
|
524
|
+
Linux)
|
|
525
|
+
command -v bwrap >/dev/null 2>&1 || missing+=' bwrap(bubblewrap)'
|
|
526
|
+
command -v socat >/dev/null 2>&1 || missing+=' socat'
|
|
527
|
+
if [[ -n "$missing" ]]; then
|
|
528
|
+
PREFLIGHT_NG+="■ 囲いの部品が足りません:${missing}"$'\n'
|
|
529
|
+
PREFLIGHT_NG+=" (無いと、職人が安全のため起動を断ります)"$'\n'
|
|
530
|
+
if command -v apt-get >/dev/null 2>&1; then
|
|
531
|
+
PREFLIGHT_NG+=" 入れる1行(Ubuntu のパスワードを聞かれます):"$'\n'
|
|
532
|
+
PREFLIGHT_NG+=" sudo apt-get install -y bubblewrap socat"$'\n'
|
|
533
|
+
# ★WSL かどうか(検査は CONDUCTOR_PROC_VERSION_FILE で見本を渡せる・既定は本物)
|
|
534
|
+
if [[ -r "${CONDUCTOR_PROC_VERSION_FILE:-/proc/version}" ]] && grep -qi microsoft "${CONDUCTOR_PROC_VERSION_FILE:-/proc/version}"; then
|
|
535
|
+
PREFLIGHT_NG+=" sudo のパスワードが分からないとき(Windows の PowerShell で打つ):"$'\n'
|
|
536
|
+
PREFLIGHT_NG+=" wsl.exe -u root apt-get install -y bubblewrap socat"$'\n'
|
|
537
|
+
fi
|
|
538
|
+
else
|
|
539
|
+
PREFLIGHT_NG+=" この機の入れ方で bubblewrap と socat を入れてください(apt-get が見つかりません)。"$'\n'
|
|
540
|
+
fi
|
|
541
|
+
else
|
|
542
|
+
PREFLIGHT_INFO+="✓ 囲いの部品: bwrap・socat"$'\n'
|
|
543
|
+
fi
|
|
544
|
+
;;
|
|
545
|
+
Darwin)
|
|
546
|
+
PREFLIGHT_INFO+="✓ 囲いの部品: Mac は OS に組み込み(追加の部品は要りません)"$'\n'
|
|
547
|
+
;;
|
|
548
|
+
*)
|
|
549
|
+
PREFLIGHT_INFO+="⚠ 囲いの部品: この機(${os})では確かめられませんでした(止めずに進みます)。"$'\n'
|
|
550
|
+
;;
|
|
551
|
+
esac
|
|
552
|
+
[[ -z "$PREFLIGHT_NG" ]]
|
|
553
|
+
}
|
|
554
|
+
# <<< preflight <<<
|
|
555
|
+
|
|
390
556
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
391
557
|
# ⓪ 引数と土台の確認
|
|
392
558
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
@@ -475,6 +641,22 @@ MSG
|
|
|
475
641
|
;;
|
|
476
642
|
esac
|
|
477
643
|
|
|
644
|
+
# --- 職人が始まるのに要る物(★先頭で確かめる・台本は自分で入れない) -----------
|
|
645
|
+
# Claude Code がまだ無いのはかまわない(⑥で入れる)。古いとき・囲いの部品が無いときは止まる。
|
|
646
|
+
PREFLIGHT_GUIDE=''
|
|
647
|
+
if preflight_check later; then
|
|
648
|
+
printf '%s' "$PREFLIGHT_INFO" | sed 's/^/ /'
|
|
649
|
+
else
|
|
650
|
+
printf '%s' "$PREFLIGHT_INFO" | sed 's/^/ /'
|
|
651
|
+
PREFLIGHT_GUIDE="$(printf '職人が始まるのに要る物が足りません。次の1行を打ってください。\n\n%s' "$PREFLIGHT_NG")"
|
|
652
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
653
|
+
printf '%s\n' "$PREFLIGHT_GUIDE" | sed 's/^/ /'
|
|
654
|
+
plan '足りない物があるため、ここで案内して止まります'
|
|
655
|
+
else
|
|
656
|
+
halt "$PREFLIGHT_GUIDE"
|
|
657
|
+
fi
|
|
658
|
+
fi
|
|
659
|
+
|
|
478
660
|
# --- WSL のときは systemd(常駐の仕組み)が要る -------------------------------
|
|
479
661
|
# 【なぜ止めるか】systemd が無いと、采配くんの常駐(指示を受け取り続ける係)を
|
|
480
662
|
# 登録できない。ここを飛ばして先へ進めると、設置は成功したように見えて
|
|
@@ -710,6 +892,11 @@ else
|
|
|
710
892
|
record '職人: 入れた'
|
|
711
893
|
fi
|
|
712
894
|
|
|
895
|
+
# ★入っていた・入れた職人が、最低版以上か(古いと中継が仕事を始めない)。
|
|
896
|
+
if [[ "$DRY_RUN" != "1" ]] && ! preflight_check need; then
|
|
897
|
+
halt "$(printf '職人が始まるのに要る物が足りません。次の1行を打ってください。\n\n%s' "$PREFLIGHT_NG")"
|
|
898
|
+
fi
|
|
899
|
+
|
|
713
900
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
714
901
|
# ⑦ 職人のログイン(★お客さまの手番・ここで止まることがある)
|
|
715
902
|
# ═════════════════════════════════════════════════════════════════════════════
|
package/sales-template/setup.sh
CHANGED
|
@@ -264,7 +264,9 @@ sed_replace() { # sed_replace <置換式> <ファイル>
|
|
|
264
264
|
# ★常駐の生死を OS ごとに確かめる(自己点検⑦で使う)。
|
|
265
265
|
service_is_running() { # service_is_running <systemd のユニット名> <launchd のラベル>
|
|
266
266
|
if [[ "$OS_KIND" == "mac" ]]; then
|
|
267
|
-
|
|
267
|
+
# ★grep -q は一致した時点で読むのをやめ、launchctl の残りの書き込みが SIGPIPE(141)になる。
|
|
268
|
+
# pipefail の下ではそれが「動いていない」に化けるので、grep に最後まで読ませる。
|
|
269
|
+
launchctl list 2>/dev/null | grep "[[:space:]]$2\$" >/dev/null
|
|
268
270
|
else
|
|
269
271
|
systemctl --user is-active --quiet "$1"
|
|
270
272
|
fi
|
|
@@ -28,6 +28,12 @@ import summary_ja # noqa: E402
|
|
|
28
28
|
|
|
29
29
|
failures = []
|
|
30
30
|
|
|
31
|
+
# ★機体に依らなくする: 採配くんの職人の中で流すと、中継が渡した CONDUCTOR_SITE・
|
|
32
|
+
# CONDUCTOR_MACHINE が入っていて、cwd から決まるはずの現場名を上書きする
|
|
33
|
+
# (ノートPCでだけ NG 3件・CI では無い)。ここで外し、上書きの試験は値を注入して確かめる。
|
|
34
|
+
for _k in ('CONDUCTOR_SITE', 'CONDUCTOR_MACHINE'):
|
|
35
|
+
os.environ.pop(_k, None)
|
|
36
|
+
|
|
31
37
|
|
|
32
38
|
def check(label, got, want):
|
|
33
39
|
ok = got == want
|
|
@@ -1186,6 +1192,11 @@ _NEV = {'hook_event_name': 'Notification', 'notification_type': 'permission_prom
|
|
|
1186
1192
|
'cwd': '/home/fshim/projects/devlog-tracker', 'session_id': 'sess-9'}
|
|
1187
1193
|
_pay = notification_hook.build_payload(_NEV)
|
|
1188
1194
|
check('現場名が入る', _pay['site'], 'devlog-tracker')
|
|
1195
|
+
os.environ['CONDUCTOR_SITE'] = 'override-site'
|
|
1196
|
+
try:
|
|
1197
|
+
check('現場名は CONDUCTOR_SITE が cwd より先に効く', notification_hook.build_payload(_NEV)['site'], 'override-site')
|
|
1198
|
+
finally:
|
|
1199
|
+
os.environ.pop('CONDUCTOR_SITE', None)
|
|
1189
1200
|
check('sessionId は空でない', bool(_pay['sessionId']), True)
|
|
1190
1201
|
check('端末で押すことが本文に書いてある', '端末' in _pay['question'], True)
|
|
1191
1202
|
check('管制からは押せないと書いてある', '管制からは押せません' in _pay['question'], True)
|
package/scripts/update-guard.sh
CHANGED
|
@@ -80,6 +80,217 @@ info() { echo "[update-guard] $*"; }
|
|
|
80
80
|
warn() { echo "[update-guard] 注意: $*" >&2; }
|
|
81
81
|
fail() { echo "[update-guard] エラー: $*" >&2; exit 1; }
|
|
82
82
|
|
|
83
|
+
# >>> preflight >>> ★この印の間が正本。install.sh と update-guard.sh で1文字も違わないことをテストが固定する。
|
|
84
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
85
|
+
# 先頭の確かめ(職人が始まるのに要る物・便 ff8f4be9)
|
|
86
|
+
#
|
|
87
|
+
# 【なぜ要るか(実機 2026-10-02・ノートPC)】
|
|
88
|
+
# 便 7901973d が3回続けて起動に失敗した。囲いの部品 bubblewrap・socat が入っておらず、
|
|
89
|
+
# 設定 sandbox.failIfUnavailable のため職人が起動を断っていた。更新までは気づかれずに動き、
|
|
90
|
+
# 更新の後に職人が始まらなくなった。また、最低版(えふさん確定 2.1.282)より古い
|
|
91
|
+
# Claude Code では中継が職人を始めない。=設定を配る前にここで気づき、人が打つ1行を出す。
|
|
92
|
+
#
|
|
93
|
+
# 【決め】
|
|
94
|
+
# ・確かめるだけ。★台本が自分で sudo を打たない・勝手に入れない(人の窓)。
|
|
95
|
+
# ・Claude Code は中継(src/headless.ts の resolveClaudeBin)と同じ場所を同じ順で探し、
|
|
96
|
+
# いちばん新しい版で判定する(CONDUCTOR_CLAUDE_BIN の指定があればそれだけ)。
|
|
97
|
+
# ・囲いの部品は、その機の仕組みに要る物だけを見る。
|
|
98
|
+
# Linux(WSL を含む)… bwrap と socat
|
|
99
|
+
# Mac … OS に組み込み(追加の部品は要らない)=何も求めない
|
|
100
|
+
# それ以外 … 止めずに「確かめられませんでした」と出す
|
|
101
|
+
# ・出す1行は、そのままコピペで動く形(山かっこの穴埋めなし)。
|
|
102
|
+
#
|
|
103
|
+
# 【使い方】preflight_check <claude が無いときの扱い: need|later>
|
|
104
|
+
# 結果: PREFLIGHT_NG(足りない物の説明と1行。空なら通過)/PREFLIGHT_INFO(止めない注意)
|
|
105
|
+
# /PREFLIGHT_CLAUDE_BIN(中継が使うはずの claude・見つからなければ空)
|
|
106
|
+
# ★later … 後の段で Claude Code を入れる台本(1行インストーラ)用。無いことだけは止めない
|
|
107
|
+
# (古いときは止める)。
|
|
108
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
109
|
+
# ★src/headless.ts の CLAUDE_MIN_VERSION と同じ値(テストで一致を固定)。
|
|
110
|
+
PREFLIGHT_CLAUDE_MIN='2.1.282'
|
|
111
|
+
|
|
112
|
+
# a が b より古いか(数字3つだけを見る。読めない字は 0=古い扱い)。
|
|
113
|
+
preflight_ver_lt() {
|
|
114
|
+
local a="${1%%-*}" b="${2%%-*}" i x y
|
|
115
|
+
local -a A B
|
|
116
|
+
IFS='.' read -r -a A <<< "$a"
|
|
117
|
+
IFS='.' read -r -a B <<< "$b"
|
|
118
|
+
for i in 0 1 2; do
|
|
119
|
+
x="${A[i]:-0}"; y="${B[i]:-0}"
|
|
120
|
+
[[ "$x" =~ ^[0-9]+$ ]] || x=0
|
|
121
|
+
[[ "$y" =~ ^[0-9]+$ ]] || y=0
|
|
122
|
+
if (( x < y )); then return 0; fi
|
|
123
|
+
if (( x > y )); then return 1; fi
|
|
124
|
+
done
|
|
125
|
+
return 1
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# 中継と同じ候補を同じ順で出す(1行に1つ)。
|
|
129
|
+
preflight_claude_candidates() {
|
|
130
|
+
if [[ -n "${CONDUCTOR_CLAUDE_BIN:-}" ]]; then
|
|
131
|
+
printf '%s\n' "$CONDUCTOR_CLAUDE_BIN"
|
|
132
|
+
return 0
|
|
133
|
+
fi
|
|
134
|
+
local d
|
|
135
|
+
local -a nv=()
|
|
136
|
+
for d in "$HOME/.nvm/versions/node"/v*; do
|
|
137
|
+
[[ -d "$d" ]] && nv+=("${d##*/}")
|
|
138
|
+
done
|
|
139
|
+
if (( ${#nv[@]} > 0 )); then
|
|
140
|
+
# 新しい node の版から(v24.21.0 → 24 21 0 で降順)
|
|
141
|
+
while IFS= read -r d; do
|
|
142
|
+
printf '%s\n' "$HOME/.nvm/versions/node/$d/bin/claude"
|
|
143
|
+
done < <(printf '%s\n' "${nv[@]}" | sort -t. -k1.2,1nr -k2,2nr -k3,3nr)
|
|
144
|
+
fi
|
|
145
|
+
printf '%s\n' "$HOME/.local/bin/claude" "$HOME/.claude/local/claude" /usr/local/bin/claude /usr/bin/claude
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# その claude の入れ方に合う更新の1行(src/headless.ts の claudeUpdateHint と同じ見分け方)。
|
|
149
|
+
preflight_claude_update_line() {
|
|
150
|
+
local bin="$1" real="$1" t i
|
|
151
|
+
# 実体を辿る(★readlink -f は使わない=無いパスでも文字列を返すため。1段ずつ辿る)
|
|
152
|
+
for i in 1 2 3 4 5 6 7 8; do
|
|
153
|
+
[[ -L "$real" ]] || break
|
|
154
|
+
t="$(readlink "$real" 2>/dev/null || true)"
|
|
155
|
+
[[ -n "$t" ]] || break
|
|
156
|
+
case "$t" in
|
|
157
|
+
/*) real="$t" ;;
|
|
158
|
+
*) real="$(dirname "$real")/$t" ;;
|
|
159
|
+
esac
|
|
160
|
+
done
|
|
161
|
+
case "$real$bin" in
|
|
162
|
+
*/node_modules/@anthropic-ai/claude-code/*|*/.nvm/versions/node/*)
|
|
163
|
+
printf ' PATH="%s:$PATH" npm install -g @anthropic-ai/claude-code@latest\n' "$(dirname "$bin")"
|
|
164
|
+
return 0 ;;
|
|
165
|
+
esac
|
|
166
|
+
case "$real" in
|
|
167
|
+
"$HOME"/.local/share/claude/*|"$HOME"/.claude/local/*)
|
|
168
|
+
printf ' "%s" update\n' "$bin"
|
|
169
|
+
return 0 ;;
|
|
170
|
+
esac
|
|
171
|
+
printf ' (入れ方を見分けられませんでした。どちらか合う方を打ってください)\n'
|
|
172
|
+
printf ' ネイティブ導入なら: "%s" update\n' "$bin"
|
|
173
|
+
printf ' npm 導入なら : npm install -g @anthropic-ai/claude-code@latest\n'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
preflight_check() {
|
|
177
|
+
local claude_mode="${1:-need}" c v best='' best_v='' unknown='' os missing=''
|
|
178
|
+
PREFLIGHT_NG=''
|
|
179
|
+
PREFLIGHT_INFO=''
|
|
180
|
+
PREFLIGHT_CLAUDE_BIN=''
|
|
181
|
+
|
|
182
|
+
# ── ① Claude Code が在り、最低版以上か ──
|
|
183
|
+
while IFS= read -r c; do
|
|
184
|
+
[[ -n "$c" && -x "$c" ]] || continue
|
|
185
|
+
v="$("$c" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n 1 || true)"
|
|
186
|
+
if [[ -z "$v" ]]; then
|
|
187
|
+
[[ -n "$unknown" ]] || unknown="$c"
|
|
188
|
+
continue
|
|
189
|
+
fi
|
|
190
|
+
if [[ -z "$best_v" ]] || preflight_ver_lt "$best_v" "$v"; then
|
|
191
|
+
best="$c"; best_v="$v"
|
|
192
|
+
fi
|
|
193
|
+
done < <(preflight_claude_candidates)
|
|
194
|
+
|
|
195
|
+
# shellcheck disable=SC2034 # update-guard.sh の ⓪-b が使う(install.sh では使わない)
|
|
196
|
+
PREFLIGHT_CLAUDE_BIN="${best:-$unknown}"
|
|
197
|
+
if [[ -n "$best_v" ]]; then
|
|
198
|
+
if preflight_ver_lt "$best_v" "$PREFLIGHT_CLAUDE_MIN"; then
|
|
199
|
+
PREFLIGHT_NG+="■ 職人のソフト(Claude Code)が古いです(${best} = ${best_v}、必要な版 ${PREFLIGHT_CLAUDE_MIN} 以上)。"$'\n'
|
|
200
|
+
PREFLIGHT_NG+=" この機の入れ方に合う1行:"$'\n'
|
|
201
|
+
PREFLIGHT_NG+="$(preflight_claude_update_line "$best")"$'\n'
|
|
202
|
+
else
|
|
203
|
+
PREFLIGHT_INFO+="✓ 職人のソフト(Claude Code): ${best} = ${best_v}(必要な版 ${PREFLIGHT_CLAUDE_MIN} 以上)"$'\n'
|
|
204
|
+
fi
|
|
205
|
+
elif [[ -n "$unknown" ]]; then
|
|
206
|
+
# ★中継も版の読めない claude は止めずに使う。ここでも止めない(作りをそろえる)。
|
|
207
|
+
PREFLIGHT_INFO+="⚠ 職人のソフト(Claude Code)の版を確かめられませんでした(${unknown})。"$'\n'
|
|
208
|
+
elif [[ "$claude_mode" == "need" ]]; then
|
|
209
|
+
PREFLIGHT_NG+="■ 職人のソフト(Claude Code)が見つかりません。"$'\n'
|
|
210
|
+
PREFLIGHT_NG+=" 入れる1行:"$'\n'
|
|
211
|
+
PREFLIGHT_NG+=" npm install -g @anthropic-ai/claude-code@latest"$'\n'
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
# ── ② 囲いの部品(その機の仕組みに要る物だけ) ──
|
|
215
|
+
os="$(uname -s 2>/dev/null || echo unknown)"
|
|
216
|
+
case "$os" in
|
|
217
|
+
Linux)
|
|
218
|
+
command -v bwrap >/dev/null 2>&1 || missing+=' bwrap(bubblewrap)'
|
|
219
|
+
command -v socat >/dev/null 2>&1 || missing+=' socat'
|
|
220
|
+
if [[ -n "$missing" ]]; then
|
|
221
|
+
PREFLIGHT_NG+="■ 囲いの部品が足りません:${missing}"$'\n'
|
|
222
|
+
PREFLIGHT_NG+=" (無いと、職人が安全のため起動を断ります)"$'\n'
|
|
223
|
+
if command -v apt-get >/dev/null 2>&1; then
|
|
224
|
+
PREFLIGHT_NG+=" 入れる1行(Ubuntu のパスワードを聞かれます):"$'\n'
|
|
225
|
+
PREFLIGHT_NG+=" sudo apt-get install -y bubblewrap socat"$'\n'
|
|
226
|
+
# ★WSL かどうか(検査は CONDUCTOR_PROC_VERSION_FILE で見本を渡せる・既定は本物)
|
|
227
|
+
if [[ -r "${CONDUCTOR_PROC_VERSION_FILE:-/proc/version}" ]] && grep -qi microsoft "${CONDUCTOR_PROC_VERSION_FILE:-/proc/version}"; then
|
|
228
|
+
PREFLIGHT_NG+=" sudo のパスワードが分からないとき(Windows の PowerShell で打つ):"$'\n'
|
|
229
|
+
PREFLIGHT_NG+=" wsl.exe -u root apt-get install -y bubblewrap socat"$'\n'
|
|
230
|
+
fi
|
|
231
|
+
else
|
|
232
|
+
PREFLIGHT_NG+=" この機の入れ方で bubblewrap と socat を入れてください(apt-get が見つかりません)。"$'\n'
|
|
233
|
+
fi
|
|
234
|
+
else
|
|
235
|
+
PREFLIGHT_INFO+="✓ 囲いの部品: bwrap・socat"$'\n'
|
|
236
|
+
fi
|
|
237
|
+
;;
|
|
238
|
+
Darwin)
|
|
239
|
+
PREFLIGHT_INFO+="✓ 囲いの部品: Mac は OS に組み込み(追加の部品は要りません)"$'\n'
|
|
240
|
+
;;
|
|
241
|
+
*)
|
|
242
|
+
PREFLIGHT_INFO+="⚠ 囲いの部品: この機(${os})では確かめられませんでした(止めずに進みます)。"$'\n'
|
|
243
|
+
;;
|
|
244
|
+
esac
|
|
245
|
+
[[ -z "$PREFLIGHT_NG" ]]
|
|
246
|
+
}
|
|
247
|
+
# <<< preflight <<<
|
|
248
|
+
|
|
249
|
+
info "⓪ 職人が始まるのに要る物の確かめ(★設定を配る前・ここで足りなければ1バイトも配りません)"
|
|
250
|
+
if preflight_check need; then
|
|
251
|
+
printf '%s' "$PREFLIGHT_INFO" | sed 's/^/[update-guard] /'
|
|
252
|
+
else
|
|
253
|
+
printf '%s' "$PREFLIGHT_INFO" | sed 's/^/[update-guard] /'
|
|
254
|
+
printf '%s' "$PREFLIGHT_NG" | sed 's/^/[update-guard] /' >&2
|
|
255
|
+
if [[ "$SYNC_ONLY" == "1" ]]; then
|
|
256
|
+
# ★同期モード(無人・1時間ごと)は設定の中身を作り変えない(番人のコードと配線だけ)。
|
|
257
|
+
# ここで止めると番人の直しまで届かなくなるので、知らせて続ける。
|
|
258
|
+
warn "職人が始まるのに要る物が足りません(上記)。同期モードなので番人のコードと配線だけ続けます。"
|
|
259
|
+
else
|
|
260
|
+
warn "職人が始まるのに要る物が足りません(上記)。★設定は1バイトも配っていません。"
|
|
261
|
+
warn " 上の1行を打ってから、もう一度このスクリプトを流してください。"
|
|
262
|
+
exit 1
|
|
263
|
+
fi
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
# ─── ⓪-b 職人が管制へ知らせる口(ai-kana の MCP)が使えるか(★確認だけのときだけ・止めない) ───
|
|
267
|
+
# 【なぜ要るか(実機 2026-10-02・便 fb248f6e)】職人の会話に ai-kana の道具が1つも出ず、完了の知らせ
|
|
268
|
+
# (conductor_complete)も開発履歴の読み取りもできなかった。便を配る前に人が気づけるよう、
|
|
269
|
+
# 確認だけのときに `claude mcp list` の結果を1行で出す。
|
|
270
|
+
# ★確認だけ(APPLY なし・同期モードでない)のときだけ流す=適用・1時間ごとの同期では外へ問い合わせない。
|
|
271
|
+
# ★鍵・トークンの値は読まない・出さない(出すのは「つながっているか」だけ)。
|
|
272
|
+
if [[ "$APPLY" != "1" && "$SYNC_ONLY" != "1" && -n "${PREFLIGHT_CLAUDE_BIN:-}" ]]; then
|
|
273
|
+
info "⓪-b 職人が管制へ知らせる口(ai-kana)の確かめ(確認だけ・止めません)"
|
|
274
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
275
|
+
mcp_out="$(timeout 60 "$PREFLIGHT_CLAUDE_BIN" mcp list 2>/dev/null || true)"
|
|
276
|
+
else
|
|
277
|
+
mcp_out="$("$PREFLIGHT_CLAUDE_BIN" mcp list 2>/dev/null || true)"
|
|
278
|
+
fi
|
|
279
|
+
mcp_ok="$(printf '%s\n' "$mcp_out" | grep -E '^(claude\.ai )?ai-kana: .*Connected' | sed -E 's/: .* - /: /' || true)"
|
|
280
|
+
mcp_ng="$(printf '%s\n' "$mcp_out" | grep -E '^(claude\.ai )?ai-kana: ' | grep -v 'Connected' | sed -E 's/: .* - /: /' || true)"
|
|
281
|
+
if [[ -n "$mcp_ok" ]]; then
|
|
282
|
+
printf '%s\n' "$mcp_ok" | sed 's/^/[update-guard] ✓ /'
|
|
283
|
+
[[ -z "$mcp_ng" ]] || printf '%s\n' "$mcp_ng" | sed 's/^/[update-guard] (予備・使っていない)/'
|
|
284
|
+
else
|
|
285
|
+
warn "職人が管制へ知らせる口(ai-kana)がつながっていません。便は動きますが、完了の知らせと開発履歴の読み書きができません。"
|
|
286
|
+
[[ -z "$mcp_ng" ]] || printf '%s\n' "$mcp_ng" | sed 's/^/[update-guard] /' >&2
|
|
287
|
+
warn " 人の手番(1手ずつ): ① Ubuntu の画面で claude と打つ ② 画面の中で /mcp と打つ"
|
|
288
|
+
warn " ③ ai-kana を選んで Enter → Authenticate を選ぶ ④ 開いたブラウザでログインする ⑤ /exit で閉じる"
|
|
289
|
+
warn " (claude.ai ai-kana のほうは https://claude.ai の設定 → コネクタ で ai-kana を有効にする)"
|
|
290
|
+
fi
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
|
|
83
294
|
# ─── ①-src 配り元を決める(★公開済みの正規版だけ・便 4061dd71) ──────────────
|
|
84
295
|
# 【なぜ要るか(実測 2026-09-16・開発履歴 6b405ae6)】
|
|
85
296
|
# 1時間ごとの自動同期の本体が /home/fshim/projects/conductor/scripts/update-guard.sh
|