@tyhld/conductor 0.12.11 → 0.12.12
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/package.json +2 -1
- package/sales-template/systemd/install.sh +50 -42
- package/scripts/conductor-run.sh +80 -0
- package/scripts/dist-source.sh +204 -15
- package/scripts/hooks/skill_sync_notify.py +6 -0
- package/scripts/update-guard.sh +20 -0
- package/systemd/ear-install.sh +49 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tyhld/conductor",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.12",
|
|
4
4
|
"description": "采配くん管制の見守りアプリ(conductor-agent)。各PCで常駐し、中央(devlog-tracker)へ定期的に生存報告(heartbeat)を送る常駐CLI。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"!scripts/hooks/__pycache__",
|
|
22
22
|
"scripts/update-guard.sh",
|
|
23
23
|
"scripts/dist-source.sh",
|
|
24
|
+
"scripts/conductor-run.sh",
|
|
24
25
|
"scripts/codex",
|
|
25
26
|
"!scripts/codex/__pycache__",
|
|
26
27
|
"skills",
|
|
@@ -105,51 +105,45 @@ if ! PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" command
|
|
|
105
105
|
fi
|
|
106
106
|
echo "[install] systemd PATH での node 解決: OK"
|
|
107
107
|
|
|
108
|
-
# --- 2)
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
CONDUCTOR_SRC=""
|
|
125
|
-
|
|
126
|
-
if [[ -f "$REPO_CLI" ]]; then
|
|
127
|
-
# 開発機。node のフルパスを頭に付ける(systemd はシェルの PATH を継承しないため)。
|
|
128
|
-
NODE_BIN="$(command -v node || true)"
|
|
129
|
-
[[ -n "$NODE_BIN" && -L "$NODE_BIN" ]] && NODE_BIN="$(readlink -f "$NODE_BIN")"
|
|
130
|
-
[[ -n "$NODE_BIN" ]] || NODE_BIN="/usr/bin/env node"
|
|
131
|
-
CONDUCTOR_BIN="$NODE_BIN $REPO_CLI"
|
|
132
|
-
CONDUCTOR_SRC="この機体のリポジトリ(開発機)"
|
|
133
|
-
elif command -v conductor >/dev/null 2>&1; then
|
|
134
|
-
CONDUCTOR_BIN="$(command -v conductor)"
|
|
135
|
-
[[ -L "$CONDUCTOR_BIN" ]] && CONDUCTOR_BIN="$(readlink -f "$CONDUCTOR_BIN")"
|
|
136
|
-
CONDUCTOR_SRC="npm で入れた全体版"
|
|
108
|
+
# --- 2) 常駐を動かすコマンドを決める -----------------------------------------
|
|
109
|
+
# ★2026-09-16(えふさん確定・便 81a08d58)に決め方を変えた。
|
|
110
|
+
# 【旧】開発機(~/projects/conductor/dist/cli.js が在る)では、そのリポジトリの中身を使う。
|
|
111
|
+
# =★職人の作業ツリーの build が常駐として動いていた。職人がブランチを切り替えて build した
|
|
112
|
+
# 瞬間、配達・完了検出・生存報告が作りかけで動く。
|
|
113
|
+
# さらに node の置き場(nvm の版つき)を ExecStart に焼き込んでいたので、node の版を変えると
|
|
114
|
+
# 203/EXEC で再起動を繰り返す形だった(2026-09-04 に耳で実際に起きた)。
|
|
115
|
+
# 【新】開発機でもお客さま機でも「入っている配布物」だけを動かす。ExecStart には動かない入口
|
|
116
|
+
# (/bin/bash ~/.tyhld/bin/conductor-run)だけを書き、配布物と node は起動のたびに探す。
|
|
117
|
+
# ★作業ツリーは選ばない。作業ツリーを試すときは、サービスを使わず端末で直接動かす
|
|
118
|
+
# (端末を閉じれば終わる=入れっぱなしの上書きが残らない)。
|
|
119
|
+
. "$SCRIPT_DIR/../../scripts/dist-source.sh"
|
|
120
|
+
|
|
121
|
+
# 入口の一式を写す元=公開済みの正規版(作業ツリーからは写さない・便 4061dd71 と同じ判定)。
|
|
122
|
+
if ! dist_source_for_setup "$(cd "$SCRIPT_DIR/../.." && pwd)"; then
|
|
123
|
+
fail "常駐を動かす采配くんを確かめられません(${DIST_REASON})。先に npm i -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org を実行してください。"
|
|
137
124
|
fi
|
|
125
|
+
BUNDLE_SRC="$DIST_ROOT"
|
|
126
|
+
# ★ユニットの正本も配り元から取る(作業ツリーの写しを配らない)。
|
|
127
|
+
[[ -f "$BUNDLE_SRC/systemd/conductor@.service" ]] && UNIT_SRC="$BUNDLE_SRC/systemd/conductor@.service"
|
|
138
128
|
|
|
139
|
-
if
|
|
140
|
-
fail "
|
|
129
|
+
if ! dist_runtime_bundle_install "$BUNDLE_SRC"; then
|
|
130
|
+
fail "常駐の入口を置けませんでした: $DIST_RUNTIME_BIN_DIR"
|
|
141
131
|
fi
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
#
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
132
|
+
# 導入処理が確かめた置き場を控えに残す(systemd の中ではまずこれを見る)。
|
|
133
|
+
dist_runtime_hint_write "$BUNDLE_SRC"
|
|
134
|
+
|
|
135
|
+
CONDUCTOR_BIN="$DIST_RUNTIME_EXEC"
|
|
136
|
+
|
|
137
|
+
# ★どれを使うかを必ず1行出す(読み違えを防ぐ)。
|
|
138
|
+
if dist_runtime_resolve; then
|
|
139
|
+
echo "[install] 常駐が使う采配くん: 入っている配布物(起動のたびに探す)"
|
|
140
|
+
echo "[install] いま見つかる配布物: $DIST_ROOT(版 ${DIST_VERSION}・node ${DIST_NODE})"
|
|
141
|
+
# 実際に動くか簡易検証(警告だけ・止めない)。
|
|
142
|
+
if ! "$DIST_NODE" "$DIST_ROOT/dist/cli.js" --help >/dev/null 2>&1; then
|
|
143
|
+
warn "conductor の実行に失敗しました。インストールを確認してください。"
|
|
144
|
+
fi
|
|
145
|
+
else
|
|
146
|
+
fail "常駐を動かす采配くんを確かめられません(${DIST_REASON})。先に npm i -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org を実行してください。"
|
|
153
147
|
fi
|
|
154
148
|
|
|
155
149
|
# --- 3) ユニットファイルを配置(__CONDUCTOR_BIN__ をフルパスに置換) ----------
|
|
@@ -177,6 +171,20 @@ echo "[install] 配置: $UNIT_DST (ExecStart=$CONDUCTOR_BIN)"
|
|
|
177
171
|
# テンプレート本体も台帳に載せる(撤去時にファイルを消す対象)。
|
|
178
172
|
ledger_add "conductor@.service"
|
|
179
173
|
|
|
174
|
+
# ★ExecStart を上書きしている drop-in を退ける(便 81a08d58)。
|
|
175
|
+
# テンプレート全体の上書き(conductor@.service.d)と、この現場だけの上書きの両方を見る。
|
|
176
|
+
# 残すと、上で書いた ExecStart より後から効いて、作業ツリーを動かし続ける。
|
|
177
|
+
for d in "$UNIT_DIR/conductor@.service.d" "$UNIT_DIR/conductor@${SITE}.service.d"; do
|
|
178
|
+
if ! retired="$(dist_runtime_retire_exec_dropins "$d")"; then
|
|
179
|
+
fail "起動方法を上書きしている drop-in を退けられませんでした: $d"
|
|
180
|
+
fi
|
|
181
|
+
if [[ -n "$retired" ]]; then
|
|
182
|
+
while IFS= read -r line; do
|
|
183
|
+
echo "[install] 起動方法を上書きしていた drop-in を控えへ退けました: $line"
|
|
184
|
+
done <<< "$retired"
|
|
185
|
+
fi
|
|
186
|
+
done
|
|
187
|
+
|
|
180
188
|
# --- 4) WorkingDirectory の解決 -----------------------------------------------
|
|
181
189
|
DEFAULT_DIR="$HOME/projects/$SITE"
|
|
182
190
|
DROPIN_DIR="$UNIT_DIR/conductor@${SITE}.service.d"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# conductor-run — 常駐(conductor@*.service)と耳(conductor-ear.service)の入口(★便 81a08d58)。
|
|
4
|
+
#
|
|
5
|
+
# 【なぜ要るか(実測 2026-09-16・開発履歴 4061dd71 の発見5)】
|
|
6
|
+
# この機(開発機)の常駐と耳は、職人の【作業ツリー】の build を動かしていた。
|
|
7
|
+
# 常駐 … conductor@.service の ExecStart が ~/projects/conductor/dist/cli.js
|
|
8
|
+
# 耳 … 配布物を指す元の定義に、作業ツリーを指す上書き(drop-in)が掛かっていた
|
|
9
|
+
# 職人がブランチを切り替えて build した瞬間、配達・完了検出・生存報告が作りかけで動く。
|
|
10
|
+
# さらにどちらも node の置き場(nvm の v24.15.0)を ExecStart に焼き込んでいたので、
|
|
11
|
+
# node の版を変えると 203/EXEC で再起動を繰り返す(2026-09-04 に耳で実際に起きた)。
|
|
12
|
+
#
|
|
13
|
+
# 【えふさん確定(2026-09-16)】「常駐・耳の配布物化 … 推しどおりで承認です」
|
|
14
|
+
# (推し=配布物を指すように揃える。全現場へ配る話ではないので、影響はこの機の中だけ)
|
|
15
|
+
#
|
|
16
|
+
# 【どう動くか】
|
|
17
|
+
# systemd は ExecStart に「/bin/bash <この入口>」だけを持つ(/bin/bash は動かない場所)。
|
|
18
|
+
# この入口が【起動のたびに】入っている配布物とその node を探し、そこへ exec する。
|
|
19
|
+
# =node の版を変えても、次の起動で新しい置き場を見つける。
|
|
20
|
+
# 探し方は毎時同期(update-guard.sh)と同じ dist-source.sh を使う=同じ機で別の版を選ばない。
|
|
21
|
+
#
|
|
22
|
+
# 【★作業ツリーは動かさない】探し方のどの道でも、git の作業場所は選ばない。
|
|
23
|
+
#
|
|
24
|
+
# 【見つからないときは黙って動かさない】
|
|
25
|
+
# 配布物・node・本体(dist/cli.js)のどれかが確かめられなければ【起動しない】。
|
|
26
|
+
# ① 画面(journal)に理由と人の手番を出す
|
|
27
|
+
# ② 控え(~/.tyhld/hooks/runtime-source.last)に失敗を残す
|
|
28
|
+
# ③ 管制へ知らせる(便 29612ff1・4061dd71 と同じ確認カードの口)
|
|
29
|
+
# systemd は Restart=always なので、入れ直せば次の起動で自動的に戻る。
|
|
30
|
+
#
|
|
31
|
+
# 【★公開 npm には問い合わせない】常駐は回線が切れても動き続ける必要がある。
|
|
32
|
+
# 入っている版が古いかどうかは、毎時同期が見て知らせる(同じ見張りを2つ作らない)。
|
|
33
|
+
#
|
|
34
|
+
# 【置き場】この入口は ~/.tyhld/bin/conductor-run として置かれ、隣に dist-source.sh と
|
|
35
|
+
# skill_sync_notify.py を置く(導入処理と毎時同期が配布物から写す)。
|
|
36
|
+
# ~/.tyhld は職人が書けない=入口を後から書き換えられない。
|
|
37
|
+
set -u
|
|
38
|
+
|
|
39
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
40
|
+
. "$HERE/dist-source.sh"
|
|
41
|
+
|
|
42
|
+
RUNTIME_STATE_DIR="${HOME:-}/.tyhld/hooks"
|
|
43
|
+
RUNTIME_STATE="$RUNTIME_STATE_DIR/runtime-source.state" # 最後に【動かせた】配布物
|
|
44
|
+
RUNTIME_LAST="$RUNTIME_STATE_DIR/runtime-source.last" # 直近の【試み】の結果
|
|
45
|
+
|
|
46
|
+
runtime_record() {
|
|
47
|
+
local status="$1" reason="$2" line
|
|
48
|
+
line="$(date '+%Y-%m-%dT%H:%M:%S%z') status=${status} reason=${reason} what=runtime-source version=${DIST_VERSION:-unknown} method=${DIST_METHOD:-none} root=${DIST_ROOT:-none} node=${DIST_NODE:-none} role=${ROLE:-unknown}"
|
|
49
|
+
mkdir -p "$RUNTIME_STATE_DIR" 2>/dev/null || return 0
|
|
50
|
+
printf '%s\n' "$line" > "$RUNTIME_LAST" 2>/dev/null || true
|
|
51
|
+
if [[ "$status" == "ok" ]]; then
|
|
52
|
+
printf '%s\n' "$line" > "$RUNTIME_STATE" 2>/dev/null || true
|
|
53
|
+
fi
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# 役(start / ear)を記録に載せる(どのサービスが止まったかを外から分かるように)。
|
|
57
|
+
ROLE="${1:-unknown}"
|
|
58
|
+
|
|
59
|
+
if ! dist_runtime_resolve; then
|
|
60
|
+
echo "[conductor-run] 起動しません: 動かす采配くんを確かめられません(${DIST_REASON})。" >&2
|
|
61
|
+
case "$DIST_REASON" in
|
|
62
|
+
not-installed) echo "[conductor-run] この機に @tyhld/conductor が入っていません。" >&2 ;;
|
|
63
|
+
worktree-refused) echo "[conductor-run] ★作業ツリーは動かしません(配布物だけを動かします)。" >&2 ;;
|
|
64
|
+
incomplete) echo "[conductor-run] 入っている配布物の中身が欠けています。" >&2 ;;
|
|
65
|
+
no-version) echo "[conductor-run] 入っている配布物の版が読めません。" >&2 ;;
|
|
66
|
+
no-node) echo "[conductor-run] 配布物を動かす node が見つかりません。" >&2 ;;
|
|
67
|
+
esac
|
|
68
|
+
echo "[conductor-run] 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org" >&2
|
|
69
|
+
runtime_record failed "$DIST_REASON"
|
|
70
|
+
if [[ -f "$HERE/skill_sync_notify.py" ]]; then
|
|
71
|
+
PYTHONPATH="$RUNTIME_STATE_DIR${PYTHONPATH:+:$PYTHONPATH}" \
|
|
72
|
+
python3 "$HERE/skill_sync_notify.py" "$RUNTIME_LAST" >/dev/null 2>&1 || true
|
|
73
|
+
fi
|
|
74
|
+
# 78 = 設定の不備(EX_CONFIG)。systemd の記録で他の失敗と見分けられるようにする。
|
|
75
|
+
exit 78
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
echo "[conductor-run] 配布物で動かします: ${DIST_ROOT}(版 ${DIST_VERSION}・node ${DIST_NODE}・見つけ方 ${DIST_METHOD})" >&2
|
|
79
|
+
runtime_record ok started
|
|
80
|
+
exec "$DIST_NODE" "$DIST_ROOT/dist/cli.js" "$@"
|
package/scripts/dist-source.sh
CHANGED
|
@@ -49,13 +49,62 @@ DIST_VERSION='' # そこに入っている版
|
|
|
49
49
|
DIST_PUBLISHED='' # 公開 npm のいまの版
|
|
50
50
|
DIST_REASON='' # 配れないときの理由(1語・記録にそのまま載る)
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
#: どの道で置き場を見つけたか(override / path / hint / scan / npm)。記録と画面に出す。
|
|
53
|
+
DIST_METHOD=''
|
|
54
|
+
#: dist_pkg_find が見つけた置き場(まだ確かめる前の候補)。
|
|
55
|
+
DIST_FOUND=''
|
|
56
|
+
|
|
57
|
+
# ★人が打った導入処理で見つけた置き場の控え(便 81a08d58)。
|
|
58
|
+
# systemd の中(常駐・耳・毎時同期)は PATH に nvm の node も conductor も居ない。
|
|
59
|
+
# そこでまずこの控えを見る。控えは ~/.tyhld の下=職人は書き換えられない。
|
|
60
|
+
DIST_RUNTIME_HINT="${DIST_RUNTIME_HINT:-${HOME:-}/.tyhld/bin/runtime-root}"
|
|
61
|
+
|
|
62
|
+
# 置き場として使ってよいか(配布物の形で、git の作業場所ではない)。
|
|
63
|
+
dist_pkg_usable() {
|
|
64
|
+
local root="$1"
|
|
65
|
+
[[ -n "$root" && -f "$root/package.json" ]] || return 1
|
|
66
|
+
# ★作業ツリーの根には .git が在る。npm で入れた配布物には無い。
|
|
67
|
+
[[ -e "$root/.git" ]] && return 1
|
|
68
|
+
return 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# 決め打ちの置き場の候補を、新しい node から順に並べる。★PATH に頼らない(便 81a08d58)。
|
|
72
|
+
# 【なぜ要るか(実測 2026-09-16)】systemd の中の PATH には nvm の node が無い。
|
|
73
|
+
# いま毎時同期が配布物を見つけられているのは、/usr/local/bin/node が
|
|
74
|
+
# nvm の v24.15.0 を指す近道になっていて、それを通して npm が置き場を答えているから。
|
|
75
|
+
# ★node の版を変えるとこの近道が古いまま残り、見つけられなくなる(2026-09-04 の 203/EXEC と同じ形)。
|
|
76
|
+
# 【どう決めるか】nvm の置き場を直接見て、配布物が入っている node の版を新しい順に並べる。
|
|
77
|
+
# node を入れ替えて古い版を消せば、その版は候補から消える=付いていく。
|
|
78
|
+
dist_pkg_candidates() {
|
|
79
|
+
local h="${HOME:-}" v d
|
|
80
|
+
if [[ -n "$h" && -d "$h/.nvm/versions/node" ]]; then
|
|
81
|
+
while IFS= read -r v; do
|
|
82
|
+
[[ -n "$v" ]] || continue
|
|
83
|
+
d="$h/.nvm/versions/node/$v/lib/node_modules/$CONDUCTOR_PKG"
|
|
84
|
+
dist_pkg_usable "$d" && printf '%s\n' "$d"
|
|
85
|
+
done < <(ls -1 "$h/.nvm/versions/node" 2>/dev/null | sort -rV)
|
|
86
|
+
fi
|
|
87
|
+
for d in "$h/.npm-global/lib/node_modules/$CONDUCTOR_PKG" \
|
|
88
|
+
"$h/.local/lib/node_modules/$CONDUCTOR_PKG" \
|
|
89
|
+
"/usr/local/lib/node_modules/$CONDUCTOR_PKG" \
|
|
90
|
+
"/usr/lib/node_modules/$CONDUCTOR_PKG"; do
|
|
91
|
+
dist_pkg_usable "$d" && printf '%s\n' "$d"
|
|
92
|
+
done
|
|
93
|
+
return 0
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# この機に入っている配布物の置き場を探す(★結果は DIST_FOUND と DIST_METHOD に入れる)。
|
|
97
|
+
# 順番:
|
|
98
|
+
# 1. 試験用の名指し(CONDUCTOR_DIST_ROOT)
|
|
99
|
+
# 2. いま PATH に居る conductor(人が打ったとき=いちばん確か)
|
|
100
|
+
# 3. 人が打ったときに書き残した控え(systemd の中ではまずこれ)
|
|
101
|
+
# 4. 決め打ちの置き場(nvm を新しい版から・その他の決め打ち)
|
|
102
|
+
# 5. npm が言う全体の置き場
|
|
103
|
+
# ★どの道でも、git の作業場所は置き場にしない。
|
|
104
|
+
dist_pkg_find() {
|
|
105
|
+
DIST_FOUND=''; DIST_METHOD=''
|
|
56
106
|
if [[ -n "${CONDUCTOR_DIST_ROOT:-}" ]]; then
|
|
57
|
-
|
|
58
|
-
return 0
|
|
107
|
+
DIST_FOUND="$CONDUCTOR_DIST_ROOT"; DIST_METHOD='override'; return 0
|
|
59
108
|
fi
|
|
60
109
|
local bin real root
|
|
61
110
|
# ★bash で動く書き方(command -v -a は zsh 専用。便 9858450b で踏んだ)。
|
|
@@ -64,18 +113,88 @@ dist_pkg_root() {
|
|
|
64
113
|
real="$(readlink -f "$bin" 2>/dev/null || printf '%s' "$bin")"
|
|
65
114
|
# <置き場>/dist/cli.js から2つ上がると配布物の根になる。
|
|
66
115
|
root="$(cd "$(dirname "$real")/.." 2>/dev/null && pwd)" || root=''
|
|
67
|
-
if
|
|
68
|
-
|
|
69
|
-
|
|
116
|
+
if dist_pkg_usable "$root"; then
|
|
117
|
+
DIST_FOUND="$root"; DIST_METHOD='path'; return 0
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
120
|
+
if [[ -f "$DIST_RUNTIME_HINT" ]]; then
|
|
121
|
+
root="$(head -n 1 "$DIST_RUNTIME_HINT" 2>/dev/null)"
|
|
122
|
+
if dist_pkg_usable "$root"; then
|
|
123
|
+
DIST_FOUND="$root"; DIST_METHOD='hint'; return 0
|
|
70
124
|
fi
|
|
71
125
|
fi
|
|
72
|
-
|
|
126
|
+
root="$(dist_pkg_candidates | head -n 1)"
|
|
127
|
+
if [[ -n "$root" ]]; then
|
|
128
|
+
DIST_FOUND="$root"; DIST_METHOD='scan'; return 0
|
|
129
|
+
fi
|
|
73
130
|
local groot
|
|
74
131
|
groot="$(npm root -g 2>/dev/null)"
|
|
75
|
-
if [[ -n "$groot" &&
|
|
76
|
-
|
|
77
|
-
|
|
132
|
+
if [[ -n "$groot" ]] && dist_pkg_usable "$groot/$CONDUCTOR_PKG"; then
|
|
133
|
+
DIST_FOUND="$groot/$CONDUCTOR_PKG"; DIST_METHOD='npm'; return 0
|
|
134
|
+
fi
|
|
135
|
+
return 1
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
# この機に入っている配布物の置き場(見つからなければ空)。★昔からの呼び方を残す。
|
|
139
|
+
dist_pkg_root() {
|
|
140
|
+
dist_pkg_find && printf '%s' "$DIST_FOUND"
|
|
141
|
+
return 0
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# 導入処理(人が打つ)が確かめた置き場を控えに残す。★導入処理からだけ呼ぶ。
|
|
145
|
+
# 渡すのは、導入処理が入口の一式を写した元=公開済みの正規版(dist_source_for_setup の答え)。
|
|
146
|
+
# お客さまの機では、導入処理は入っている配布物の中から動くので、その置き場そのものになる。
|
|
147
|
+
# ★systemd の中(常駐・耳・毎時同期)では書かない=自分の推測を自分の根拠にしない。
|
|
148
|
+
# ★使えない置き場(git の作業場所・形が違う)は書かない。
|
|
149
|
+
dist_runtime_hint_write() {
|
|
150
|
+
local root="$1"
|
|
151
|
+
dist_pkg_usable "$root" || return 0
|
|
152
|
+
mkdir -p "$(dirname "$DIST_RUNTIME_HINT")" 2>/dev/null || return 0
|
|
153
|
+
printf '%s\n' "$root" > "$DIST_RUNTIME_HINT" 2>/dev/null || true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# 置き場に対応する node を返す(★その置き場を持つ node を使う=版が食い違わない)。
|
|
157
|
+
dist_node_for() {
|
|
158
|
+
local root="$1" prefix
|
|
159
|
+
if [[ -n "${CONDUCTOR_DIST_NODE:-}" ]]; then
|
|
160
|
+
printf '%s' "$CONDUCTOR_DIST_NODE"; return 0
|
|
161
|
+
fi
|
|
162
|
+
prefix="${root%/lib/node_modules/$CONDUCTOR_PKG}"
|
|
163
|
+
if [[ "$prefix" != "$root" && -x "$prefix/bin/node" ]]; then
|
|
164
|
+
printf '%s' "$prefix/bin/node"; return 0
|
|
165
|
+
fi
|
|
166
|
+
# 予備=置き場に node が同居しない入れ方(npm-global など)。
|
|
167
|
+
local n
|
|
168
|
+
n="$(command -v node 2>/dev/null || true)"
|
|
169
|
+
[[ -n "$n" ]] && { printf '%s' "$n"; return 0; }
|
|
170
|
+
return 1
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#: 常駐・耳を動かす node(dist_runtime_resolve が決める)。
|
|
174
|
+
DIST_NODE=''
|
|
175
|
+
|
|
176
|
+
# 常駐と耳を動かす置き場を決める(便 81a08d58)。★作業ツリーは選ばない。
|
|
177
|
+
# 返り 0 … DIST_ROOT / DIST_NODE で動かしてよい
|
|
178
|
+
# 返り 1 … 動かしてはいけない(DIST_REASON に理由)
|
|
179
|
+
# ★公開 npm には問い合わせない。常駐は回線が切れても動き続ける必要があり、
|
|
180
|
+
# 版が古いかどうかは毎時同期(dist_source_resolve)が見て知らせる=同じ見張りを2つ作らない。
|
|
181
|
+
dist_runtime_resolve() {
|
|
182
|
+
DIST_ROOT=''; DIST_NODE=''; DIST_VERSION=''; DIST_PUBLISHED=''; DIST_REASON=''
|
|
183
|
+
if ! dist_pkg_find; then
|
|
184
|
+
DIST_REASON='not-installed'; return 1
|
|
185
|
+
fi
|
|
186
|
+
if ! dist_pkg_usable "$DIST_FOUND"; then
|
|
187
|
+
DIST_REASON='worktree-refused'; return 1
|
|
188
|
+
fi
|
|
189
|
+
if ! dist_source_complete "$DIST_FOUND" || [[ ! -f "$DIST_FOUND/dist/cli.js" ]]; then
|
|
190
|
+
DIST_REASON='incomplete'; return 1
|
|
78
191
|
fi
|
|
192
|
+
DIST_VERSION="$(dist_installed_version "$DIST_FOUND")"
|
|
193
|
+
[[ -n "$DIST_VERSION" ]] || { DIST_REASON='no-version'; return 1; }
|
|
194
|
+
DIST_NODE="$(dist_node_for "$DIST_FOUND")" || DIST_NODE=''
|
|
195
|
+
[[ -n "$DIST_NODE" ]] || { DIST_REASON='no-node'; return 1; }
|
|
196
|
+
DIST_ROOT="$DIST_FOUND"
|
|
197
|
+
return 0
|
|
79
198
|
}
|
|
80
199
|
|
|
81
200
|
# 指定の置き場に入っている版(package.json)。読めなければ空。
|
|
@@ -114,11 +233,12 @@ dist_published_version() {
|
|
|
114
233
|
dist_source_resolve() {
|
|
115
234
|
DIST_ROOT=''; DIST_VERSION=''; DIST_PUBLISHED=''; DIST_REASON=''
|
|
116
235
|
local root
|
|
117
|
-
|
|
118
|
-
if
|
|
236
|
+
# ★常駐・耳(dist_runtime_resolve)と同じ探し方を使う=同じ機で別の版を選ばない。
|
|
237
|
+
if ! dist_pkg_find; then
|
|
119
238
|
DIST_REASON='not-installed'
|
|
120
239
|
return 1
|
|
121
240
|
fi
|
|
241
|
+
root="$DIST_FOUND"
|
|
122
242
|
DIST_VERSION="$(dist_installed_version "$root")"
|
|
123
243
|
if [[ -z "$DIST_VERSION" ]]; then
|
|
124
244
|
DIST_REASON='no-version'
|
|
@@ -138,6 +258,75 @@ dist_source_resolve() {
|
|
|
138
258
|
return 0
|
|
139
259
|
}
|
|
140
260
|
|
|
261
|
+
# ─── 常駐・耳の入口(便 81a08d58)──────────────────────────────────────────
|
|
262
|
+
#: 入口を置く場所。★node の版が変わっても動かない場所(~/.tyhld の下・職人は書けない)。
|
|
263
|
+
DIST_RUNTIME_BIN_DIR="${DIST_RUNTIME_BIN_DIR:-${HOME:-}/.tyhld/bin}"
|
|
264
|
+
#: systemd の ExecStart に書く起動の頭。★/bin/bash と上の入口だけ=どちらも動かない場所。
|
|
265
|
+
#: %h は systemd が起動時に HOME へ置き換える。
|
|
266
|
+
DIST_RUNTIME_EXEC='/bin/bash %h/.tyhld/bin/conductor-run'
|
|
267
|
+
|
|
268
|
+
# 入口の一式(入口・探し方・知らせる係)を、配布物から写す。
|
|
269
|
+
# 返り 0 … 写せて、中身が配布物と同じことを確かめた
|
|
270
|
+
# 返り 1 … 写せなかった(呼び出し側は ExecStart を書き換えない)
|
|
271
|
+
dist_runtime_bundle_install() {
|
|
272
|
+
local src="$1" dst="${2:-$DIST_RUNTIME_BIN_DIR}"
|
|
273
|
+
local pairs=(
|
|
274
|
+
"scripts/conductor-run.sh:conductor-run"
|
|
275
|
+
"scripts/dist-source.sh:dist-source.sh"
|
|
276
|
+
"scripts/hooks/skill_sync_notify.py:skill_sync_notify.py"
|
|
277
|
+
)
|
|
278
|
+
mkdir -p "$dst" 2>/dev/null || return 1
|
|
279
|
+
local pair from to
|
|
280
|
+
for pair in "${pairs[@]}"; do
|
|
281
|
+
from="$src/${pair%%:*}"
|
|
282
|
+
to="$dst/${pair##*:}"
|
|
283
|
+
[[ -f "$from" ]] || return 1
|
|
284
|
+
cp -f "$from" "$to.tmp" 2>/dev/null || return 1
|
|
285
|
+
mv -f "$to.tmp" "$to" 2>/dev/null || return 1 # 入れ替えは原子的=起動中に半端な入口を読ませない
|
|
286
|
+
cmp -s "$from" "$to" || return 1
|
|
287
|
+
done
|
|
288
|
+
chmod 755 "$dst/conductor-run" 2>/dev/null || return 1
|
|
289
|
+
return 0
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
# ExecStart を上書きしている drop-in を、日時つきの控えへ退ける(便 81a08d58)。
|
|
293
|
+
# 【なぜ要るか】耳には「作業ツリーを動かす」上書き(conductor-ear.service.d/override.conf)が
|
|
294
|
+
# 残っている。正本の ExecStart を直しても、この上書きが後から効いて作業ツリーを動かし続ける。
|
|
295
|
+
# 【何をするか】<ユニット>.d/*.conf のうち、ExecStart= の行を持つものだけを
|
|
296
|
+
# <名前>.bak.<日時> へ改名する(systemd は .conf 以外を読まない=効かなくなる)。
|
|
297
|
+
# ★環境変数や作業場所だけを上書きする drop-in には触らない。
|
|
298
|
+
# ★消さない。settings.json と同じ流儀の控えとして残す(中身を後から読める)。
|
|
299
|
+
# 出力: 退けたファイルの場所を1行ずつ。
|
|
300
|
+
dist_runtime_retire_exec_dropins() {
|
|
301
|
+
local dropin_dir="$1" f
|
|
302
|
+
[[ -d "$dropin_dir" ]] || return 0
|
|
303
|
+
for f in "$dropin_dir"/*.conf; do
|
|
304
|
+
[[ -f "$f" ]] || continue
|
|
305
|
+
if grep -qE '^[[:space:]]*ExecStart[[:space:]]*=' "$f" 2>/dev/null; then
|
|
306
|
+
local bak
|
|
307
|
+
bak="$f.bak.$(date '+%Y%m%d-%H%M%S')"
|
|
308
|
+
mv -f "$f" "$bak" 2>/dev/null || return 1
|
|
309
|
+
printf '%s\n' "$bak"
|
|
310
|
+
fi
|
|
311
|
+
done
|
|
312
|
+
return 0
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
# 明示された起動コマンドが作業ツリーを指していないか(★警告に使う)。
|
|
316
|
+
dist_runtime_bin_is_worktree() {
|
|
317
|
+
local cmd="$1" w
|
|
318
|
+
for w in $cmd; do
|
|
319
|
+
case "$w" in
|
|
320
|
+
*/dist/cli.js)
|
|
321
|
+
local r
|
|
322
|
+
r="$(cd "$(dirname "$w")/.." 2>/dev/null && pwd)" || continue
|
|
323
|
+
[[ -e "$r/.git" ]] && return 0
|
|
324
|
+
;;
|
|
325
|
+
esac
|
|
326
|
+
done
|
|
327
|
+
return 1
|
|
328
|
+
}
|
|
329
|
+
|
|
141
330
|
# 配布物として体裁が揃っているか(配る中身が欠けた置き場を掴まない)。
|
|
142
331
|
# ★ここで見るのは「采配くんの配布物であること」を決める最小の3つだけ。
|
|
143
332
|
# 職人の鉄則(skills/)は安全境界ではなく作業規律で、欠けたときの扱いは ①-d が持っている
|
|
@@ -60,6 +60,12 @@ NOTICE_KINDS = {
|
|
|
60
60
|
'stopped': '職人の決まり(鉄則)が、新しい版に入れ替わっていません。'
|
|
61
61
|
'職人は古い決まりのまま動きます。',
|
|
62
62
|
},
|
|
63
|
+
'runtime-source': {
|
|
64
|
+
'key': 'runtime-source-missing',
|
|
65
|
+
'summary': '常駐と耳を起動できません',
|
|
66
|
+
'stopped': '常駐(配達と生存報告)と耳(指示の着信合図)を動かす采配くん本体が'
|
|
67
|
+
'見つからないため、起動を止めています。管制への配達が止まります。',
|
|
68
|
+
},
|
|
63
69
|
'dist-source': {
|
|
64
70
|
'key': 'dist-source-unverified',
|
|
65
71
|
'summary': '安全装置を配れていません',
|
package/scripts/update-guard.sh
CHANGED
|
@@ -412,6 +412,26 @@ else
|
|
|
412
412
|
info " (予定)正本の内容へ揃えます(版 ${CRAFTSMAN_VER} / 中身 ${CRAFTSMAN_SHA})"
|
|
413
413
|
fi
|
|
414
414
|
|
|
415
|
+
# ─── ①-g 常駐と耳の入口を最新へ揃える(機体1回・★同期モードでも行う・便 81a08d58) ───
|
|
416
|
+
# 【なぜ要るか】常駐と耳は ~/.tyhld/bin/conductor-run(入口)から動く。入口と、その隣の
|
|
417
|
+
# 探し方(dist-source.sh)・知らせる係は、導入処理のときに配布物から写したもの。
|
|
418
|
+
# 配布物を直しても、ここで揃えないと入口は古いまま残る。
|
|
419
|
+
# ★入口がまだ無い機体(導入処理を流し直していない機体)には新しく置かない。
|
|
420
|
+
# 常駐と耳がまだ入口を使っていないので、置いても意味が無く、機体に物を増やすだけになる。
|
|
421
|
+
# ★動いている常駐と耳は再起動しない。入口は起動のたびに読まれるので、次の起動から効く。
|
|
422
|
+
if [[ -f "$DIST_RUNTIME_BIN_DIR/conductor-run" ]]; then
|
|
423
|
+
info "①-g 常駐と耳の入口: $DIST_RUNTIME_BIN_DIR"
|
|
424
|
+
if [[ "$APPLY" == "1" ]]; then
|
|
425
|
+
if dist_runtime_bundle_install "$DIST_ROOT"; then
|
|
426
|
+
info " 揃えました(配布物 ${DIST_ROOT}・版 ${DIST_VERSION})"
|
|
427
|
+
else
|
|
428
|
+
warn "常駐と耳の入口を揃えられませんでした: $DIST_RUNTIME_BIN_DIR(★古い入口のまま動きます)"
|
|
429
|
+
fi
|
|
430
|
+
else
|
|
431
|
+
info " (予定)配布物の入口の一式へ揃えます"
|
|
432
|
+
fi
|
|
433
|
+
fi
|
|
434
|
+
|
|
415
435
|
# ★同期モードは ①-b・①-c・①-d2 を飛ばす(掃除・タイマー・pnpm 点検は「番人のコード」ではない)。
|
|
416
436
|
# ★鉄則スキル(①-d)は上で【同期モードでも】行う(便 29612ff1)。
|
|
417
437
|
# 飛ばす分は、人が APPLY=1 で通常どおり流したときに従来どおり行われる。
|
package/systemd/ear-install.sh
CHANGED
|
@@ -59,42 +59,45 @@ if ! command -v systemctl >/dev/null 2>&1 || ! systemctl --user show-environment
|
|
|
59
59
|
fi
|
|
60
60
|
|
|
61
61
|
# --- 3) 耳を動かすコマンドを決める -------------------------------------------
|
|
62
|
-
# ★
|
|
63
|
-
#
|
|
62
|
+
# ★2026-09-16(えふさん確定・便 81a08d58)に決め方を変えた。
|
|
63
|
+
# 【旧】開発機(このスクリプトの隣に dist/cli.js が在る)では、そのリポジトリの中身を使う。
|
|
64
|
+
# =★職人の作業ツリーの build が耳として動いていた。職人がブランチを切り替えて build した
|
|
65
|
+
# 瞬間、指示の着信合図が作りかけで動く。
|
|
66
|
+
# さらに node の置き場(nvm の版つき)を ExecStart に焼き込んでいたので、node の版を変えると
|
|
67
|
+
# 203/EXEC で再起動を繰り返した(2026-09-04 に実際に起きた)。
|
|
68
|
+
# 【新】既定は「入っている配布物」だけを動かす。ExecStart には動かない入口
|
|
69
|
+
# (/bin/bash ~/.tyhld/bin/conductor-run)だけを書き、配布物と node は起動のたびに探す。
|
|
70
|
+
# ★開発機でもお客さま機でも同じ。作業ツリーは選ばない。
|
|
71
|
+
# 【作業ツリーを試したいとき】引数で起動コマンドを明示したときだけ、それをそのまま使う
|
|
72
|
+
# (人が打った物に限る)。作業ツリーを指していれば★警告を出す。
|
|
73
|
+
# ふだんの試し方は、サービスを使わずに端末で直接動かすこと(端末を閉じれば終わる=
|
|
74
|
+
# 入れっぱなしの上書きが残らない)。
|
|
75
|
+
. "$SCRIPT_DIR/../scripts/dist-source.sh"
|
|
64
76
|
EAR_BIN="${1:-}"
|
|
65
77
|
EAR_SRC="引数で指定"
|
|
66
78
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
# 【なぜ①を先に見るか】以前は npm の全体版を先に掴んでいた。開発機で
|
|
72
|
-
# `npm i -g @tyhld/conductor` を一度でも打つと、耳がリポジトリではなく
|
|
73
|
-
# 【配布済みの古い版】で動いてしまう。直したはずの不具合が現場で再現し続け、
|
|
74
|
-
# 原因を「直っていない」と読み違える。リポジトリが在る機体では
|
|
75
|
-
# リポジトリを正とする(conductor@ の導入処理と同じ順番に揃えてある)。
|
|
76
|
-
if [[ -z "$EAR_BIN" ]]; then
|
|
77
|
-
# 配布物・リポジトリのどちらでも、このスクリプトから見て ../dist/cli.js に本体がある。
|
|
78
|
-
CLI_JS="$(cd "$SCRIPT_DIR/.." && pwd)/dist/cli.js"
|
|
79
|
-
if [[ -f "$CLI_JS" ]]; then
|
|
80
|
-
NODE_BIN="$(command -v node || true)"
|
|
81
|
-
[[ -n "$NODE_BIN" && -L "$NODE_BIN" ]] && NODE_BIN="$(readlink -f "$NODE_BIN")"
|
|
82
|
-
[[ -n "$NODE_BIN" ]] || NODE_BIN="/usr/bin/env node"
|
|
83
|
-
EAR_BIN="$NODE_BIN $CLI_JS"
|
|
84
|
-
EAR_SRC="この機体のリポジトリ(開発機)"
|
|
79
|
+
if [[ -n "$EAR_BIN" ]]; then
|
|
80
|
+
if dist_runtime_bin_is_worktree "$EAR_BIN"; then
|
|
81
|
+
warn "★明示された起動コマンドは作業ツリーを指しています: $EAR_BIN"
|
|
82
|
+
warn " 作りかけの build が耳として動きます。試し終わったら、引数なしでこの台本を流し直してください。"
|
|
85
83
|
fi
|
|
84
|
+
else
|
|
85
|
+
# 入口の一式を写す元=公開済みの正規版(作業ツリーからは写さない・便 4061dd71 と同じ判定)。
|
|
86
|
+
if ! dist_source_for_setup "$(cd "$SCRIPT_DIR/.." && pwd)"; then
|
|
87
|
+
skip "耳を動かす采配くんを確かめられません(${DIST_REASON})。
|
|
88
|
+
人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org"
|
|
89
|
+
fi
|
|
90
|
+
if ! dist_runtime_bundle_install "$DIST_ROOT"; then
|
|
91
|
+
skip "耳の入口を置けませんでした: $DIST_RUNTIME_BIN_DIR"
|
|
92
|
+
fi
|
|
93
|
+
# 導入処理が確かめた置き場を控えに残す(systemd の中ではまずこれを見る)。
|
|
94
|
+
dist_runtime_hint_write "$DIST_ROOT"
|
|
95
|
+
# ★ユニットの正本も配り元から取る(作業ツリーの写しを配らない)。
|
|
96
|
+
[[ -f "$DIST_ROOT/systemd/$UNIT_NAME" ]] && UNIT_SRC="$DIST_ROOT/systemd/$UNIT_NAME"
|
|
97
|
+
EAR_BIN="$DIST_RUNTIME_EXEC"
|
|
98
|
+
EAR_SRC="入っている配布物(起動のたびに探す・版 ${DIST_VERSION:-不明})"
|
|
86
99
|
fi
|
|
87
100
|
|
|
88
|
-
if [[ -z "$EAR_BIN" ]] && command -v conductor >/dev/null 2>&1; then
|
|
89
|
-
# お客さま機: npm グローバルの conductor(実体パスまで解決する)。
|
|
90
|
-
resolved="$(command -v conductor)"
|
|
91
|
-
[[ -L "$resolved" ]] && resolved="$(readlink -f "$resolved")"
|
|
92
|
-
EAR_BIN="$resolved"
|
|
93
|
-
EAR_SRC="npm で入れた全体版"
|
|
94
|
-
fi
|
|
95
|
-
|
|
96
|
-
[[ -n "$EAR_BIN" ]] || skip "耳を動かすコマンドが見つかりません(conductor も dist/cli.js も不在)。"
|
|
97
|
-
|
|
98
101
|
# ★どちらを使ったかを必ず1行出す(読み違えを防ぐ)。
|
|
99
102
|
info "耳が使う采配くん: ${EAR_SRC}"
|
|
100
103
|
info "耳の起動コマンド: $EAR_BIN ear"
|
|
@@ -108,9 +111,23 @@ fi
|
|
|
108
111
|
mv -f "$UNIT_DST.tmp" "$UNIT_DST" # 置換は原子的=壊れかけのユニットを残さない
|
|
109
112
|
info "配置: $UNIT_DST"
|
|
110
113
|
|
|
114
|
+
# ★ExecStart を上書きしている drop-in を退ける(便 81a08d58)。
|
|
115
|
+
# 残すと、上で書いた ExecStart より後から効いて、作業ツリーを動かし続ける。
|
|
116
|
+
if retired="$(dist_runtime_retire_exec_dropins "$UNIT_DIR/$UNIT_NAME.d")"; then
|
|
117
|
+
if [[ -n "$retired" ]]; then
|
|
118
|
+
while IFS= read -r line; do
|
|
119
|
+
info "起動方法を上書きしていた drop-in を控えへ退けました: $line"
|
|
120
|
+
done <<< "$retired"
|
|
121
|
+
fi
|
|
122
|
+
else
|
|
123
|
+
skip "起動方法を上書きしている drop-in を退けられませんでした: $UNIT_DIR/$UNIT_NAME.d"
|
|
124
|
+
fi
|
|
125
|
+
|
|
111
126
|
# --- 5) 反映して起動 ----------------------------------------------------------
|
|
112
127
|
systemctl --user daemon-reload || skip "daemon-reload に失敗しました。"
|
|
113
128
|
|
|
129
|
+
# ★動いている耳は、再起動するまで古い起動方法のまま。ここでは再起動しない
|
|
130
|
+
# (動いている便を途中で切らないため・人が頃合いを見て再起動する)。
|
|
114
131
|
if systemctl --user enable --now "$UNIT_NAME"; then
|
|
115
132
|
info "有効化: $UNIT_NAME(このPCに1本・合図を受けて各現場の relay を前倒しします)"
|
|
116
133
|
# ★台帳へ載せるのは enable に成功したときだけ。
|
|
@@ -125,6 +142,8 @@ fi
|
|
|
125
142
|
loginctl enable-linger "$USER" >/dev/null 2>&1 || true
|
|
126
143
|
|
|
127
144
|
echo
|
|
145
|
+
echo "★新しい起動方法は、耳を再起動したときから効きます:"
|
|
146
|
+
echo " systemctl --user restart conductor-ear"
|
|
128
147
|
echo "ログ確認: journalctl --user -u conductor-ear -f"
|
|
129
148
|
echo "停止: systemctl --user disable --now conductor-ear"
|
|
130
149
|
systemctl --user --no-pager status "$UNIT_NAME" 2>/dev/null || true
|