@tyhld/conductor 0.12.85 → 0.12.86

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.
@@ -20,8 +20,16 @@
20
20
  #
21
21
  # 【この部品が決めること】★これだけ
22
22
  # ・配ってよい中身の置き場(DIST_ROOT)=この機に入っている @tyhld/conductor の配布物
23
- # ・それが「公開済みの正規版」か(入っている版 == 公開 npm のいまの版)
24
- # ・違う/確かめられないときは【配らない】と言い切ること
23
+ # ・それが「公開されている正規の版で、管制で不具合の印が無い版」か(★便 29babc5d で変えた)
24
+ # 以前は「入っている版 == 公開 npm のいまの版(latest)」だけを配っていた。自社PCの段階配布では
25
+ # 後続の機が数時間 latest より古い(正規の)版のまま動くので、そのあいだ番人が止まってしまう。
26
+ # 【えふさん確定(2026-10-02 夜・判断の窓1)】「入っている版=公開の最新」→
27
+ # 「公開されている正規の版で、管制で不具合の印が無い版」。
28
+ # 正規の版 … 公開 npm にその版が在り、「使わないで」の印(npm deprecate)が付いていない
29
+ # 管制の印 … 管制の目標の版の口に入っている版を添えて聞く(installedConductorFlag)。
30
+ # "suspect"・"bad" なら配らない。欄が無い・"unknown"・管制に届かないときは
31
+ # 「印が無い」として配る(★番人を配らないほうが危ない)
32
+ # ・違う/確かめられないときは【配らない】と言い切ること(公開 npm に聞けないときも配らない=今までどおり)
25
33
  #
26
34
  # 【この部品が決めないこと】
27
35
  # ・番人の判定・禁止の中身・鉄則の中身(1文字も触らない)
@@ -36,6 +44,8 @@
36
44
  # 【試験用の入口】★安全境界ではない(環境変数を置ける人は cp も打てる)。
37
45
  # CONDUCTOR_DIST_ROOT … 配布物の置き場を名指しする(試験で偽の配布物を指すため)
38
46
  # CONDUCTOR_DIST_PUBLISHED … 公開 npm への問い合わせの代わりに使う値(試験で網を切らないため)
47
+ # CONDUCTOR_DIST_PUBLISHED_VERSIONS … 同じく「公開 npm に在る版」の一覧(空白区切り・既定は上の1つだけ)
48
+ # CONDUCTOR_DIST_DEPRECATED_VERSIONS … 同じく「使わないで」の印が付いた版の一覧(空白区切り)
39
49
 
40
50
  # ★取得先の宣言は install.sh / setup.sh の「registry-pin」正本と同じ形にする。
41
51
  # (公開 npm 1本。手元の設定が何であってもここだけは公開 npm を見る=便 9858450b)
@@ -48,6 +58,7 @@ DIST_ROOT='' # 配ってよい中身の置き場(確かめられたと
48
58
  DIST_VERSION='' # そこに入っている版
49
59
  DIST_PUBLISHED='' # 公開 npm のいまの版
50
60
  DIST_REASON='' # 配れないときの理由(1語・記録にそのまま載る)
61
+ DIST_CENTRAL_FLAG='' # 管制の印(ok / suspect / bad / unknown)。配り元を決めたときに入る
51
62
 
52
63
  #: どの道で置き場を見つけたか(override / path / hint / scan / npm)。記録と画面に出す。
53
64
  DIST_METHOD=''
@@ -176,8 +187,9 @@ DIST_NODE=''
176
187
  # 常駐と耳を動かす置き場を決める(便 81a08d58)。★作業ツリーは選ばない。
177
188
  # 返り 0 … DIST_ROOT / DIST_NODE で動かしてよい
178
189
  # 返り 1 … 動かしてはいけない(DIST_REASON に理由)
179
- # ★公開 npm には問い合わせない。常駐は回線が切れても動き続ける必要があり、
180
- # 版が古いかどうかは毎時同期(dist_source_resolve)が見て知らせる=同じ見張りを2つ作らない。
190
+ # ★公開 npm には問い合わせない。常駐は回線が切れても動き続ける必要がある。
191
+ # 版をそろえるのは、自動更新が入っている機(自社PC)では更新係(tyhld-autoupdate)、
192
+ # 入っていない機では毎時同期(dist_source_resolve)が見て知らせる=同じ見張りを2つ作らない。
181
193
  dist_runtime_resolve() {
182
194
  DIST_ROOT=''; DIST_NODE=''; DIST_VERSION=''; DIST_PUBLISHED=''; DIST_REASON=''
183
195
  if ! dist_pkg_find; then
@@ -270,17 +282,108 @@ dist_published_version() {
270
282
  printf '%s' "$DIST_PUBLISHED"
271
283
  }
272
284
 
285
+ #: 入っている版が公開 npm に在るか(yes / no / 空=確かめられない)と、「使わないで」の印の文。
286
+ DIST_INSTALLED_PUBLISHED=''
287
+ DIST_DEPRECATED=''
288
+
289
+ # 入っている版が「公開 npm に在る正規の版」かを確かめる(★便 29babc5d・えふさん確定 2026-10-02 夜)。
290
+ # 公開 npm に版を名指しして聞く(npm view <pkg>@<版> version deprecated --json)。
291
+ # 在る → yes(「使わないで」の印があれば DIST_DEPRECATED に入る)
292
+ # E404 → no(公開されていない=正規の版ではない)
293
+ # ほかの失敗 → 空(確かめられない)
294
+ dist_version_published_query() {
295
+ local root="$1" ver="$2" out rc parsed v
296
+ DIST_INSTALLED_PUBLISHED=''; DIST_DEPRECATED=''
297
+ if [[ -n "${CONDUCTOR_DIST_PUBLISHED+x}" ]]; then
298
+ local list="${CONDUCTOR_DIST_PUBLISHED_VERSIONS:-$CONDUCTOR_DIST_PUBLISHED}"
299
+ [[ -n "$list" ]] || return 0
300
+ DIST_INSTALLED_PUBLISHED='no'
301
+ for v in $list; do [[ "$v" == "$ver" ]] && DIST_INSTALLED_PUBLISHED='yes'; done
302
+ for v in ${CONDUCTOR_DIST_DEPRECATED_VERSIONS:-}; do [[ "$v" == "$ver" ]] && DIST_DEPRECATED='使わないでの印があります'; done
303
+ return 0
304
+ fi
305
+ [[ -n "$DIST_NPM" ]] || DIST_NPM="$(dist_npm_for "$root")" || DIST_NPM=''
306
+ [[ -n "$DIST_NPM" ]] || return 0
307
+ out="$(PATH="$(dirname "$DIST_NPM"):${PATH:-}" "$DIST_NPM" view "$CONDUCTOR_PKG@$ver" version deprecated --json "$CONDUCTOR_REGISTRY_ARG" 2>/dev/null)"
308
+ rc=$?
309
+ parsed="$(printf '%s' "$out" | python3 -c 'import json, sys
310
+ ver, rc = sys.argv[1], int(sys.argv[2])
311
+ try:
312
+ d = json.loads(sys.stdin.read() or "null")
313
+ except Exception:
314
+ d = None
315
+ if rc == 0 and d == ver:
316
+ print("yes|")
317
+ elif rc == 0 and isinstance(d, dict) and d.get("version") == ver:
318
+ dep = d.get("deprecated") or ""
319
+ print("yes|" + (dep if isinstance(dep, str) else "使わないでの印があります").replace("\n", " ").replace("|", "/"))
320
+ elif isinstance(d, dict) and isinstance(d.get("error"), dict) and d["error"].get("code") == "E404":
321
+ print("no|")
322
+ else:
323
+ print("|")' "$ver" "$rc" 2>/dev/null)"
324
+ [[ "$parsed" == *'|'* ]] || return 0
325
+ DIST_INSTALLED_PUBLISHED="${parsed%%|*}"
326
+ DIST_DEPRECATED="${parsed#*|}"
327
+ return 0
328
+ }
329
+
330
+ # 管制に「入っている版に不具合の印が無いか」を聞く(★便 29babc5d)。結果は DIST_CENTRAL_FLAG。
331
+ # GET <管制>/api/conductor/machines/target?machine=<機体名>&conductorVersion=<入っている版>
332
+ # → 答えの installedConductorFlag が "ok" | "suspect" | "bad" | "unknown"。
333
+ # ★欄が無い・届かない・ログイン画面へ回される・接続設定が無い は、すべて unknown(=印が無い)。
334
+ # ★接続設定は環境変数、無ければ ~/.conductor.env の CONDUCTOR_URL・CONDUCTOR_TOKEN・CONDUCTOR_MACHINE
335
+ # の3つだけを読む(★値は画面にも控えにも出さない・この関数の外へ持ち出さない)。
336
+ dist_central_flag_query() {
337
+ local ver="$1" url="${CONDUCTOR_URL:-}" tok="${CONDUCTOR_TOKEN:-}" machine="${CONDUCTOR_MACHINE:-}"
338
+ local envf="${HOME:-}/.conductor.env" line key val got
339
+ DIST_CENTRAL_FLAG='unknown'
340
+ if [[ ( -z "$url" || -z "$tok" ) && -r "$envf" ]]; then
341
+ while IFS= read -r line || [[ -n "$line" ]]; do
342
+ line="${line#export }"
343
+ key="${line%%=*}"; val="${line#*=}"
344
+ val="${val%\"}"; val="${val#\"}"; val="${val%\'}"; val="${val#\'}"
345
+ case "$key" in
346
+ CONDUCTOR_URL) [[ -n "$url" ]] || url="$val" ;;
347
+ CONDUCTOR_TOKEN) [[ -n "$tok" ]] || tok="$val" ;;
348
+ CONDUCTOR_MACHINE) [[ -n "$machine" ]] || machine="$val" ;;
349
+ esac
350
+ done < "$envf"
351
+ fi
352
+ [[ -n "$url" && -n "$tok" && -n "$ver" ]] || return 0
353
+ [[ -n "$machine" ]] || machine="$(hostname 2>/dev/null)"
354
+ got="$(DCF_URL="$url" DCF_KEY="$tok" DCF_MACHINE="$machine" DCF_VERSION="$ver" python3 -c 'import json, os, urllib.parse, urllib.request
355
+ class NoRedirect(urllib.request.HTTPRedirectHandler):
356
+ def redirect_request(self, *a, **k):
357
+ return None
358
+ q = urllib.parse.urlencode({"machine": os.environ["DCF_MACHINE"], "conductorVersion": os.environ["DCF_VERSION"]})
359
+ req = urllib.request.Request(os.environ["DCF_URL"].rstrip("/") + "/api/conductor/machines/target?" + q,
360
+ headers={"Authorization": "Bearer " + os.environ["DCF_KEY"]})
361
+ try:
362
+ with urllib.request.build_opener(NoRedirect).open(req, timeout=15) as r:
363
+ if "application/json" in (r.headers.get("content-type") or ""):
364
+ f = json.loads(r.read().decode("utf-8")).get("installedConductorFlag")
365
+ print(f if f in ("ok", "suspect", "bad", "unknown") else "unknown")
366
+ except Exception:
367
+ pass' 2>/dev/null)" || got=''
368
+ case "$got" in ok|suspect|bad|unknown) DIST_CENTRAL_FLAG="$got" ;; *) DIST_CENTRAL_FLAG='unknown' ;; esac
369
+ return 0
370
+ }
371
+
273
372
  # 配ってよい中身の置き場を決める。
274
373
  # 返り 0 … DIST_ROOT を配り元にしてよい
275
374
  # 返り 1 … 配ってはいけない(DIST_REASON に理由)
276
375
  # 理由の語(記録にそのまま載る)
277
- # not-installed … 配布物がこの機に入っていない
278
- # no-version … 入っている配布物の版が読めない
279
- # no-npm … 公開 npm に問い合わせる npm が見つからない(配布物の node の隣にも PATH にも無い)
280
- # unverifiable … npm は在るが、公開 npm への問い合わせに失敗した(回線・取得先・npm の不調)
281
- # version-drift … 入っている版が公開 npm のいまの版と違う
376
+ # not-installed … 配布物がこの機に入っていない
377
+ # no-version … 入っている配布物の版が読めない
378
+ # no-npm … 公開 npm に問い合わせる npm が見つからない(配布物の node の隣にも PATH にも無い)
379
+ # unverifiable … npm は在るが、公開 npm への問い合わせに失敗した(回線・取得先・npm の不調)
380
+ # unpublished … 入っている版が公開 npm に無い(正規の版ではない)
381
+ # deprecated … 入っている版に「使わないで」の印(npm deprecate)が付いている
382
+ # flagged-suspect … 管制で「疑い」の印が付いている版
383
+ # flagged-bad … 管制で「不具合」の印が付いている版
384
+ # ★2026-10-02 まで在った version-drift(公開の最新と違う)は、もう配らない理由ではない(便 29babc5d)。
282
385
  dist_source_resolve() {
283
- DIST_ROOT=''; DIST_VERSION=''; DIST_PUBLISHED=''; DIST_REASON=''
386
+ DIST_ROOT=''; DIST_VERSION=''; DIST_PUBLISHED=''; DIST_REASON=''; DIST_CENTRAL_FLAG=''
284
387
  local root
285
388
  # ★常駐・耳(dist_runtime_resolve)と同じ探し方を使う=同じ機で別の版を選ばない。
286
389
  if ! dist_pkg_find; then
@@ -300,14 +403,60 @@ dist_source_resolve() {
300
403
  DIST_REASON="${DIST_PUBLISHED_ERR:-unverifiable}"
301
404
  return 1
302
405
  fi
303
- if [[ "$DIST_VERSION" != "$DIST_PUBLISHED" ]]; then
304
- DIST_REASON='version-drift'
406
+ # ★公開の最新と同じなら、それだけで公開 npm に在る正規の版(問い合わせを1回で済ませる)。
407
+ if [[ "$DIST_VERSION" == "$DIST_PUBLISHED" ]]; then
408
+ DIST_INSTALLED_PUBLISHED='yes'; DIST_DEPRECATED=''
409
+ else
410
+ dist_version_published_query "$root" "$DIST_VERSION"
411
+ fi
412
+ case "$DIST_INSTALLED_PUBLISHED" in
413
+ yes) ;;
414
+ no) DIST_REASON='unpublished'; return 1 ;;
415
+ *) DIST_REASON='unverifiable'; return 1 ;;
416
+ esac
417
+ if [[ -n "$DIST_DEPRECATED" ]]; then
418
+ DIST_REASON='deprecated'
305
419
  return 1
306
420
  fi
421
+ dist_central_flag_query "$DIST_VERSION"
422
+ case "$DIST_CENTRAL_FLAG" in
423
+ suspect) DIST_REASON='flagged-suspect'; return 1 ;;
424
+ bad) DIST_REASON='flagged-bad'; return 1 ;;
425
+ esac
307
426
  DIST_ROOT="$root"
308
427
  return 0
309
428
  }
310
429
 
430
+ # 自動更新が入っている機か(自社PCの鍵 ~/.tyhld/bin/autoupdate/enabled が在るか・便 29babc5d)。
431
+ dist_autoupdate_enabled() {
432
+ [[ -f "${HOME:-}/.tyhld/bin/autoupdate/enabled" ]]
433
+ }
434
+
435
+ # 版がそろっていないときに人が読む「次の1手」を1行ずつ出す(★文言の正本は1か所=ここ)。
436
+ # 【なぜ分けるか(設計 3ed9e2b2 の 3/4「いまの決まりが書いてある場所(11)」)】
437
+ # 自動更新が入っている機(自社PC)は、更新係が管制の決めた版へそろえる=人が npm で入れると
438
+ # 段階配布の順番が崩れる。入っていない機(お客さまの機・自動更新を入れる前の機)だけ、人が入れ直す。
439
+ # 引数: 理由の語(DIST_REASON)。
440
+ dist_human_step() {
441
+ local reason="${1:-}"
442
+ case "$reason" in
443
+ flagged-bad|flagged-suspect)
444
+ echo "入っている版 ${DIST_VERSION:-不明} に、管制で ${reason#flagged-} の印が付いています。"
445
+ if dist_autoupdate_enabled; then
446
+ echo "この機は自動更新が入っています。管制が直した版を決めれば、次の自動更新で入れ替わります(人の操作は要りません)。"
447
+ else
448
+ echo "人の手番: CC1 に知らせてください(直した版が公開されたら npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org)。"
449
+ fi
450
+ return 0 ;;
451
+ esac
452
+ if dist_autoupdate_enabled; then
453
+ echo "この機は自動更新が入っています。次の自動更新(管制が決めた版を、作業中の便が終わってから入れます)でそろいます。管制の「PCの状態」で確かめてください。"
454
+ echo " 自動更新が止まっているときだけ CC1 に知らせてください(人が npm で入れると段階配布の順番が崩れます)。"
455
+ else
456
+ echo "人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org"
457
+ fi
458
+ }
459
+
311
460
  # ─── 常駐・耳の入口(便 81a08d58)──────────────────────────────────────────
312
461
  #: 入口を置く場所。★node の版が変わっても動かない場所(~/.tyhld の下・職人は書けない)。
313
462
  DIST_RUNTIME_BIN_DIR="${DIST_RUNTIME_BIN_DIR:-${HOME:-}/.tyhld/bin}"
@@ -110,6 +110,11 @@ REINSTALL = ('npm install -g @tyhld/conductor '
110
110
  RESYNC = 'APPLY=1 bash "$(npm root -g)/@tyhld/conductor/scripts/update-guard.sh"'
111
111
 
112
112
 
113
+ def autoupdate_enabled():
114
+ """自動更新が入っている PC か(自社PCの鍵 ~/.tyhld/bin/autoupdate/enabled・便 29babc5d)。"""
115
+ return os.path.isfile(os.path.join(os.path.expanduser('~'), '.tyhld', 'bin', 'autoupdate', 'enabled'))
116
+
117
+
113
118
  def recovery_steps(reason):
114
119
  """うまくいかなかった理由に【合った】手順を返す(便 675e2d05)。
115
120
 
@@ -118,7 +123,20 @@ def recovery_steps(reason):
118
123
  (入っている版と公開の版が違う)のときにこれを打っても、配り元の確認でまた弾かれる=
119
124
  同じ失敗を繰り返すだけで直らない。先に入れ直しが要る。
120
125
  """
121
- if reason in ('version-drift', 'not-installed', 'incomplete'):
126
+ if reason in ('flagged-bad', 'flagged-suspect'):
127
+ # ★管制で不具合・疑いの印が付いた版(便 29babc5d)。入れ直しても同じ版なら直らない。
128
+ if autoupdate_enabled():
129
+ return ['再開のしかた: この PC は自動更新が入っています。管制が直した版を決めれば、'
130
+ '次の自動更新で入れ替わります(人の操作は要りません)。']
131
+ return ['再開のしかた: この版には管制で不具合の印が付いています。CC1 に知らせてください'
132
+ f'(直した版が公開されたら {REINSTALL})。']
133
+ if reason in ('unpublished', 'deprecated', 'version-drift', 'not-installed', 'incomplete'):
134
+ # ★自動更新が入っている PC は、更新係が管制の決めた版へそろえる(便 29babc5d)。
135
+ # 人が npm で入れると段階配布の順番が崩れるので、入れ直しを促さない。
136
+ # ★not-installed・incomplete は配布物そのものが無い=更新係も動けないので、自動更新の有無によらず入れ直す。
137
+ if autoupdate_enabled() and reason in ('unpublished', 'deprecated', 'version-drift'):
138
+ return ['再開のしかた: この PC は自動更新が入っています。次の自動更新(管制が決めた版を、'
139
+ '作業中の便が終わってから入れます)でそろいます。管制の「PCの状態」で確かめてください。']
122
140
  return [f'再開のしかた: この PC で次の順に打ってください。① {REINSTALL}',
123
141
  f' ② {RESYNC}']
124
142
  if reason == 'no-npm':
@@ -322,6 +322,32 @@ with tempfile.TemporaryDirectory() as tmp:
322
322
  check_true(f'{label}: 入っている配布物の台本を打たせる', 'npm root -g' in b)
323
323
  check(f'{label}: 作業ツリーへ cd させない', 'cd ~/projects' in b, False)
324
324
 
325
+ # ★便 29babc5d: 番人を配る条件が「公開されている正規の版で、管制で不具合の印が無い版」になった。
326
+ unpub = body_for('unpublished')
327
+ check_true('公開npmに無い版: 入れ直しから案内する', '① npm install -g @tyhld/conductor' in unpub)
328
+ check_true('使わないでの印: 入れ直しから案内する', '① npm install -g' in body_for('deprecated'))
329
+ bad = body_for('flagged-bad')
330
+ check_true('管制で不具合の印: CC1 に知らせる', 'CC1 に知らせて' in bad)
331
+ check('管制で不具合の印: 配り直しだけを促さない(同じ版なら直らない)', '② APPLY=1 bash' in bad, False)
332
+
333
+ # ★自動更新が入っている PC(鍵 ~/.tyhld/bin/autoupdate/enabled)は、人に npm で入れさせない
334
+ # (更新係が管制の決めた版へそろえる。人が入れると段階配布の順番が崩れる)。
335
+ old_home = os.environ.get('HOME')
336
+ os.environ['HOME'] = tmp
337
+ try:
338
+ os.makedirs(os.path.join(tmp, '.tyhld', 'bin', 'autoupdate'), exist_ok=True)
339
+ write(os.path.join(tmp, '.tyhld', 'bin', 'autoupdate'), 'enabled', 'x\n')
340
+ auto = body_for('unpublished')
341
+ check_true('自動更新の PC: 次の自動更新でそろうと書く', '次の自動更新' in auto)
342
+ check('自動更新の PC: npm で入れさせない', 'npm install -g' in auto, False)
343
+ check_true('自動更新の PC・不具合の印: 人の操作は要らないと書く', '人の操作は要りません' in body_for('flagged-suspect'))
344
+ check_true('自動更新の PC でも、配布物が無いときは入れ直す', 'npm install -g' in body_for('not-installed'))
345
+ finally:
346
+ if old_home is None:
347
+ os.environ.pop('HOME', None)
348
+ else:
349
+ os.environ['HOME'] = old_home
350
+
325
351
  # ── ★札の覚え書き(壊れていても・書けなくても止まらない)──────────────────
326
352
  print('\n【★札の覚え書き】')
327
353
  with tempfile.TemporaryDirectory() as tmp:
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env bash
2
+ # tyhld-autoupdate — 自社PCの更新係を1回流す入口(便 29babc5d・設計 3ed9e2b2 の A3)。
3
+ #
4
+ # 【置き場】~/.tyhld/bin/tyhld-autoupdate(★職人は書けない場所)。隣に dist-source.sh が要る
5
+ # (常駐の入口 conductor-run と同じ置き場・同じ探し方)。置くのは自社PCの導入台本
6
+ # scripts/autoupdate-install.sh だけ。★お客さまの機の導入台本(sales-template/install.sh・setup.sh)は置かない。
7
+ #
8
+ # 【使い方】systemd から呼ぶ(人が打つ物ではない)。
9
+ # tyhld-autoupdate-boot.service … ~/.tyhld/bin/tyhld-autoupdate --boot(起動時に1回・常駐より先)
10
+ # tyhld-autoupdate.service … ~/.tyhld/bin/tyhld-autoupdate(タイマーから数分ごと。聞きに行くのは管制が返す間隔ごと)
11
+ # 終了コード: 0=いつも(更新しなかった・失敗した も 0。理由は journal と控えに残る)/2=使い方の誤り/
12
+ # 78=動かす采配くんが見つからない
13
+ #
14
+ # 【決め】
15
+ # ・鍵(~/.tyhld/bin/autoupdate/enabled)が無い機では、何も読まずに終わる(お客さまの機で動かない)。
16
+ # ・同時に2つ動かさない(起動時の1回とタイマーが重なっても、後の方は何もせず終わる)。
17
+ # ・動かすのは常駐と同じ「公開済みの配布物」(作業ツリーは動かさない=dist_runtime_resolve)。
18
+ # ・接続設定は systemd が EnvironmentFile で渡す。無いときだけ ~/.conductor.env から
19
+ # CONDUCTOR_URL・CONDUCTOR_TOKEN・CONDUCTOR_MACHINE の3つを読む(★値は画面に出さない)。
20
+ set -u
21
+
22
+ KEY="${HOME:-}/.tyhld/bin/autoupdate/enabled"
23
+ if [[ ! -f "$KEY" ]]; then
24
+ echo "[tyhld-autoupdate] 自動更新の鍵がありません($KEY)。この機では何もしません。"
25
+ exit 0
26
+ fi
27
+
28
+ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
29
+ . "$HERE/dist-source.sh"
30
+
31
+ if ! dist_runtime_resolve; then
32
+ echo "[tyhld-autoupdate] 流せません: 動かす采配くんを確かめられません(${DIST_REASON})。" >&2
33
+ echo "[tyhld-autoupdate] 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org" >&2
34
+ exit 78
35
+ fi
36
+
37
+ ENV_FILE="${HOME:-}/.conductor.env"
38
+ if [[ ( -z "${CONDUCTOR_URL:-}" || -z "${CONDUCTOR_TOKEN:-}" ) && -r "$ENV_FILE" ]]; then
39
+ while IFS= read -r line || [[ -n "$line" ]]; do
40
+ line="${line#export }"
41
+ case "$line" in
42
+ CONDUCTOR_URL=*|CONDUCTOR_TOKEN=*|CONDUCTOR_MACHINE=*)
43
+ key="${line%%=*}"; val="${line#*=}"
44
+ val="${val%\"}"; val="${val#\"}"; val="${val%\'}"; val="${val#\'}"
45
+ [[ -n "${!key:-}" ]] || export "$key=$val"
46
+ ;;
47
+ esac
48
+ done < "$ENV_FILE"
49
+ fi
50
+
51
+ # ★同時に2つ動かさない(flock が無い機では、そのまま1つとして動かす)。
52
+ LOCK="${HOME:-}/.tyhld/bin/autoupdate/lock"
53
+ if command -v flock >/dev/null 2>&1; then
54
+ exec 9>"$LOCK" || exit 0
55
+ if ! flock -n 9; then
56
+ echo "[tyhld-autoupdate] ほかの更新係が動いているので、今回は何もしません。"
57
+ exit 0
58
+ fi
59
+ fi
60
+
61
+ "$DIST_NODE" "$DIST_ROOT/dist/cli.js" autoupdate "$@"
@@ -308,8 +308,10 @@ fi
308
308
  # 「止まる」を採ると、今までどおり打ったのに何も起きない形になり、手順を覚え直すことになる。
309
309
  #
310
310
  # 【正規版でないときは黙って進まない】
311
- # 入っていない/版が読めない/公開 npm に聞けない/版が公開 npm の今の版と違う のどれでも、
312
- # ★1バイトも配らずに終わる。失敗は控えに残し、便 29612ff1 で作った知らせ方(確認カード)に載せる。
311
+ # 入っていない/版が読めない/公開 npm に聞けない/版が公開 npm に無い・使わないでの印がある/
312
+ # 管制で不具合(bad)・疑い(suspect)の印がある のどれでも、★1バイトも配らずに終わる。
313
+ # ★公開の最新より古くても、公開 npm に在る正規の版で印が無ければ配る(便 29babc5d・段階配布のため・
314
+ # えふさん確定 2026-10-02 夜)。管制に届かないときは「印が無い」として配る(配らないほうが危ない)。失敗は控えに残し、便 29612ff1 で作った知らせ方(確認カード)に載せる。
313
315
  . "$SCRIPT_DIR/dist-source.sh"
314
316
 
315
317
  DIST_STATE="$GUARD_HOME/dist-source.state" # 最後に【確かめられた】配り元
@@ -349,9 +351,10 @@ if ! dist_source_resolve; then
349
351
  no-version) warn " 入っている配布物の版が読めません。" ;;
350
352
  no-npm) warn " 公開 npm に問い合わせる npm が見つかりません(配布物を動かす node の隣にも、PATH にもありません)。" ;;
351
353
  unverifiable) warn " 公開 npm への問い合わせに失敗しました(npm: ${DIST_NPM:-不明}。回線・取得先・npm の不調のどれか)。" ;;
352
- version-drift) warn " 入っている版 ${DIST_VERSION} が、公開 npm のいまの版 ${DIST_PUBLISHED} と違います。" ;;
354
+ unpublished) warn " 入っている版 ${DIST_VERSION} は公開 npm にありません(正規の版ではありません。公開の最新は ${DIST_PUBLISHED})。" ;;
355
+ deprecated) warn " 入っている版 ${DIST_VERSION} には、公開 npm で「使わないで」の印が付いています(${DIST_DEPRECATED})。" ;;
353
356
  esac
354
- warn " 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org"
357
+ while IFS= read -r l; do warn " $l"; done < <(dist_human_step "$DIST_REASON")
355
358
  dist_record failed "$DIST_REASON"
356
359
  dist_notify
357
360
  exit 0
@@ -363,7 +366,12 @@ if ! dist_source_complete "$DIST_ROOT"; then
363
366
  dist_notify
364
367
  exit 0
365
368
  fi
366
- info " 配り元: $DIST_ROOT(版 $DIST_VERSION =公開 npm のいまの版)"
369
+ if [[ "$DIST_VERSION" == "$DIST_PUBLISHED" ]]; then
370
+ info " 配り元: $DIST_ROOT(版 $DIST_VERSION =公開 npm のいまの版・管制の印 ${DIST_CENTRAL_FLAG:-unknown})"
371
+ else
372
+ # ★段階配布のあいだ、後続の機は公開の最新より古い正規の版で動く(便 29babc5d・えふさん確定 2026-10-02 夜)。
373
+ info " 配り元: $DIST_ROOT(版 $DIST_VERSION =公開 npm に在る正規の版・公開の最新は $DIST_PUBLISHED・管制の印 ${DIST_CENTRAL_FLAG:-unknown})"
374
+ fi
367
375
  if [[ "$DIST_ROOT" != "$REPO_ROOT" ]]; then
368
376
  info " ★この台本の置き場($REPO_ROOT)からは配りません。"
369
377
  fi
@@ -102,7 +102,7 @@ else
102
102
  # 入口の一式を写す元=公開済みの正規版(作業ツリーからは写さない・便 4061dd71 と同じ判定)。
103
103
  if ! dist_source_for_setup "$(cd "$SCRIPT_DIR/.." && pwd)"; then
104
104
  die "司令塔を動かす采配くんを確かめられません(${DIST_REASON})。
105
- 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org"
105
+ $(dist_human_step "$DIST_REASON" | sed 's/^/ /')"
106
106
  fi
107
107
  if ! dist_runtime_bundle_install "$DIST_ROOT"; then
108
108
  die "司令塔の入口を置けませんでした: $DIST_RUNTIME_BIN_DIR"
@@ -85,7 +85,7 @@ else
85
85
  # 入口の一式を写す元=公開済みの正規版(作業ツリーからは写さない・便 4061dd71 と同じ判定)。
86
86
  if ! dist_source_for_setup "$(cd "$SCRIPT_DIR/.." && pwd)"; then
87
87
  skip "耳を動かす采配くんを確かめられません(${DIST_REASON})。
88
- 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org"
88
+ $(dist_human_step "$DIST_REASON" | sed 's/^/ /')"
89
89
  fi
90
90
  if ! dist_runtime_bundle_install "$DIST_ROOT"; then
91
91
  skip "耳の入口を置けませんでした: $DIST_RUNTIME_BIN_DIR"
@@ -66,7 +66,7 @@ done
66
66
  . "$SCRIPT_DIR/../scripts/dist-source.sh"
67
67
  if ! dist_source_for_setup "$REPO_ROOT"; then
68
68
  warn "配り元を確かめられません(${DIST_REASON})。自動同期タイマーは設置しません。"
69
- warn " 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org"
69
+ while IFS= read -r l; do warn " $l"; done < <(dist_human_step "$DIST_REASON")
70
70
  exit 0
71
71
  fi
72
72
  if [[ "$DIST_ROOT" != "$REPO_ROOT" ]]; then
@@ -0,0 +1,27 @@
1
+ # tyhld-autoupdate-boot.service — 起動時に1回だけ、更新係を流す(便 29babc5d・設計 3ed9e2b2 の A3)。
2
+ #
3
+ # 【何をするか】~/.tyhld/bin/tyhld-autoupdate --boot を1回。管制に目標の版を聞き、違えば入れ替える。
4
+ # 起動直後は便が0なので、更新は「電源を入れた直後」にほぼ済む(設計 1/4)。
5
+ # 自己診断は、常駐が起きてからタイマーの回(tyhld-autoupdate.service)が行う。
6
+ #
7
+ # 【順番】常駐(conductor@*・耳・司令塔)は、この1回が終わってから起きる。
8
+ # ★順番は常駐の側の drop-in(conductor@.service.d/10-tyhld-autoupdate.conf の After=)で決める。
9
+ # 置くのは scripts/autoupdate-install.sh(自社PCだけ)。お客さまの機の常駐には何も足さない。
10
+ # ★この1回が失敗しても常駐は起きる(After= だけで Requires= は付けない)。
11
+ #
12
+ # 【★自社PCだけ】置いて有効にするのは scripts/autoupdate-install.sh(人の窓で1回)だけ。
13
+
14
+ [Unit]
15
+ Description=tyhld: auto-update check at boot (before the relays start)
16
+ After=network-online.target
17
+ Wants=network-online.target
18
+
19
+ [Service]
20
+ Type=oneshot
21
+ EnvironmentFile=-%h/.conductor.env
22
+ ExecStart=/bin/bash %h/.tyhld/bin/tyhld-autoupdate --boot
23
+ # 入れ替え(npm・公式の導入台本)の上限。常駐をいつまでも待たせない。
24
+ TimeoutStartSec=20min
25
+
26
+ [Install]
27
+ WantedBy=default.target
@@ -0,0 +1,24 @@
1
+ # tyhld-autoupdate.service — 更新係を1回流す(便 29babc5d・設計 3ed9e2b2 の A3)。
2
+ #
3
+ # 【何をするか】~/.tyhld/bin/tyhld-autoupdate を1回(中身は src/autoupdate.ts)。
4
+ # ・管制が返す checkAgainMin の時刻が来た回だけ、管制に目標の版を聞く(それ以外の回はすぐ終わる)
5
+ # ・全現場が空きのときだけ入れ替える → 常駐を起こし直す → 自己診断 → 結果を管制へ
6
+ # ・自己診断が FAIL なら、この機だけ前の版へ戻す
7
+ # ★sudo を使わない。番人・禁止の一覧・囲い・通信の許可には触らない。
8
+ #
9
+ # 【★自社PCだけ】置いて有効にするのは scripts/autoupdate-install.sh(人の窓で1回)だけ。
10
+ # 鍵(~/.tyhld/bin/autoupdate/enabled)が無ければ、この係は何もしない。
11
+ # .timer から数分ごとに呼ばれる(tyhld-autoupdate.timer)。
12
+
13
+ [Unit]
14
+ Description=tyhld: auto-update (conductor / Claude Code, staged by the central)
15
+ After=network-online.target
16
+ Wants=network-online.target
17
+
18
+ [Service]
19
+ Type=oneshot
20
+ EnvironmentFile=-%h/.conductor.env
21
+ ExecStart=/bin/bash %h/.tyhld/bin/tyhld-autoupdate
22
+ # 入れ替え+自己診断(小さな試験の3分を含む)+戻すときの分。
23
+ TimeoutStartSec=60min
24
+ Nice=10
@@ -0,0 +1,20 @@
1
+ # tyhld-autoupdate.timer — 更新係を数分ごとに起こす(便 29babc5d・設計 3ed9e2b2 の A3)。
2
+ #
3
+ # - OnBootSec=4min … 起動時の1回(tyhld-autoupdate-boot)と常駐が起きた後に、1回目
4
+ # (起動時に入れ替えた機は、ここで自己診断を行う)
5
+ # - OnUnitActiveSec=5min … 以後5分ごとに起きる。★管制へ聞きに行くのは、管制が返した
6
+ # checkAgainMin(いまは15分)が来た回だけ(間隔は更新係が決め打ちしない)
7
+ # - Persistent=false … 取りこぼしの追い実行はしない(起動時の1回が在る)
8
+ #
9
+ # ★自社PCだけ。有効にするのは scripts/autoupdate-install.sh(お客さまの機には入れない)。
10
+
11
+ [Unit]
12
+ Description=tyhld: auto-update check
13
+
14
+ [Timer]
15
+ OnBootSec=4min
16
+ OnUnitActiveSec=5min
17
+ AccuracySec=30s
18
+
19
+ [Install]
20
+ WantedBy=timers.target
@@ -5,7 +5,7 @@
5
5
  # ・副作用なし(commit・push・PR・合流・配備・公開をしない)。結果は手元の控えと管制へ。
6
6
  # ・正常時は何も知らせない(知らせるかどうかは管制が決める)。
7
7
  #
8
- # 【★自社PCだけ】置いて有効にするのは自社PCの導入台本(次の便 A3)か人の窓。
8
+ # 【★自社PCだけ】置いて有効にするのは自社PCの導入台本 scripts/autoupdate-install.sh(人の窓で1回)。
9
9
  # sales-template/install.sh・setup.sh はこのタイマーを置かない(お客さまの機には入れない)。
10
10
  # .timer から毎日1回呼ばれる(tyhld-selfcheck.timer)。
11
11
 
@@ -5,7 +5,7 @@
5
5
  # - RandomizedDelaySec … 複数の機が同じ時刻に Claude を呼ばないよう30分ばらす。
6
6
  # ★管制は「26時間来ない機」を🟡にする想定(設計 3ed9e2b2 の 2/4)。
7
7
  #
8
- # ★自社PCだけ。有効にするのは自社PCの導入台本(次の便 A3)か人の窓(お客さまの機には入れない)。
8
+ # ★自社PCだけ。有効にするのは自社PCの導入台本 scripts/autoupdate-install.sh(お客さまの機には入れない)。
9
9
 
10
10
  [Unit]
11
11
  Description=tyhld: daily self-check