@tyhld/conductor 0.3.0 → 0.7.0
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 +301 -19
- package/dist/cli.js +143 -18
- package/dist/ear-routing.js +57 -0
- package/dist/ear.js +57 -0
- package/dist/env-file-perm.js +67 -0
- package/dist/nudge.js +172 -0
- package/dist/realtime-parse.js +116 -0
- package/dist/realtime.js +251 -0
- package/dist/relay-runner.js +65 -0
- package/dist/relay.js +911 -134
- package/dist/websocket-transport.js +66 -0
- package/launchd/ear-install.sh +96 -0
- package/package.json +30 -1
- package/sales-template/README.md +185 -25
- package/sales-template/install.sh +890 -0
- package/sales-template/launchd/install.sh +151 -0
- package/sales-template/settings.json +26 -97
- package/sales-template/setup.sh +754 -117
- package/sales-template/systemd/README.md +28 -4
- package/sales-template/systemd/install.sh +54 -5
- package/sales-template/systemd/paste-cache-prune-install.sh +75 -0
- package/sales-template/systemd/tyhld-paste-cache-prune.service +25 -0
- package/sales-template/systemd/tyhld-paste-cache-prune.timer +19 -0
- package/sales-template/uninstall.sh +245 -0
- package/scripts/hooks/README.md +246 -0
- package/scripts/hooks/cc2_guard.py +145 -0
- package/scripts/hooks/codex-hooks.sample.json +58 -0
- package/scripts/hooks/hook_datalink.py +440 -0
- package/scripts/hooks/install-codex-hooks.sh +127 -0
- package/scripts/hooks/notification_hook.py +167 -0
- package/scripts/hooks/permission_request_hook.py +207 -0
- package/scripts/hooks/policy.py +759 -0
- package/scripts/hooks/settings.sample.json +142 -0
- package/scripts/hooks/stop_hook.py +275 -0
- package/scripts/hooks/summary_ja.py +155 -0
- package/scripts/hooks/test_hook_datalink.py +282 -0
- package/scripts/hooks/test_policy.py +1241 -0
- package/skills/conductor-craftsman/SKILL.md +40 -0
- package/systemd/conductor-ear.service +63 -0
- package/systemd/conductor@.service +62 -0
- package/systemd/ear-install.sh +131 -0
- package/systemd/guard-sync-install.sh +94 -0
- package/systemd/tyhld-guard-sync.service +28 -0
- package/systemd/tyhld-guard-sync.timer +25 -0
- package/sales-template/cc2_guard.py +0 -395
- package/sales-template/systemd/conductor@.service +0 -47
package/sales-template/setup.sh
CHANGED
|
@@ -10,13 +10,21 @@
|
|
|
10
10
|
# bash setup.sh my-app /home/user/work/my-app # ディレクトリを明示指定
|
|
11
11
|
#
|
|
12
12
|
# やること(順番に・冪等・失敗時は止めて報告):
|
|
13
|
-
# ① 前提チェック(node>=18 / tmux / git / python3>=3.8 / systemd / sudo)
|
|
13
|
+
# ① 前提チェック(node>=18 / tmux / git / python3>=3.8 / systemd / sudo / ★Claude Code)
|
|
14
14
|
# ② conductor 本体インストール(npm i -g @tyhld/conductor)
|
|
15
15
|
# ③ node symlink(/usr/local/bin/node → 実体・sudo)
|
|
16
|
-
# ④ .conductor.env
|
|
17
|
-
# ⑤
|
|
18
|
-
#
|
|
19
|
-
#
|
|
16
|
+
# ④ .conductor.env 作成(★合言葉は伏せ字で入力/★その場で1回つながるか確かめる/chmod 600)
|
|
17
|
+
# ⑤ 番人一式の配置(★登録するイベントは見本 settings.sample.json が正・直書きしない)
|
|
18
|
+
# ・番人一式を ~/.tyhld/hooks/ へ/フックは ~/.claude/settings.json に1本
|
|
19
|
+
# ・現場の .claude/settings.json には権限ルールのみ・番人フックは書かない(ADR-013)
|
|
20
|
+
# ・⑤-0 で見本と配る中身を機械照合し、外れていれば止める(★足し忘れの防止・ADR-017)
|
|
21
|
+
# + 職人の鉄則スキル conductor-craftsman を ~/.claude/skills/ へ配置
|
|
22
|
+
# ⑥ systemd install(現場ごとの常駐 conductor@<現場> + このPCに1本の耳 conductor-ear/enable + linger)
|
|
23
|
+
# ⑦ ★自己点検(置いたものを○×で出す・確認するだけ)
|
|
24
|
+
# ⑧ 職人起動の案内+★管制の画面での確かめ方
|
|
25
|
+
#
|
|
26
|
+
# ★ADR-017: 「静かに間違った成功」で終わらせない。職人が無い/合言葉が違う/アドレスが違う
|
|
27
|
+
# ときは、その場で止めて日本語で直し方を出す。
|
|
20
28
|
#
|
|
21
29
|
set -euo pipefail
|
|
22
30
|
|
|
@@ -39,17 +47,114 @@ fi
|
|
|
39
47
|
SITE="$1"
|
|
40
48
|
PROJECT_DIR="${2:-$HOME/projects/$SITE}"
|
|
41
49
|
|
|
50
|
+
# ─── 最低版(★この値は install.sh と同じにすること) ─────────────────────────
|
|
51
|
+
# 【なぜ要るか(実測 2026-09-04)】公開 npm には 0.3.0 しか出ておらず、お客さまは
|
|
52
|
+
# それを掴んでいた。0.3.0 の中身は古い鍵名 CONDUCTOR_SHARED_SECRET を読むので、
|
|
53
|
+
# いまの接続情報(CONDUCTOR_TOKEN)では「未設定です」で落ちる(migakia で再現)。
|
|
54
|
+
# =入っている版が古ければ、設置を進める前に気づいて直す。
|
|
55
|
+
# ★値は sales-template/install.sh の MIN_CONDUCTOR_VERSION と同じにすること。
|
|
56
|
+
# 食い違うと「入口は通したのに設置係が止める」が起きる。テストで同一を固定してある。
|
|
57
|
+
MIN_CONDUCTOR_VERSION="0.5.0"
|
|
58
|
+
|
|
42
59
|
# ─── ヘルパ ───────────────────────────────────────────────────────────────────
|
|
43
60
|
fail() { echo; echo "[setup] エラー: $1" >&2; exit 1; }
|
|
44
61
|
warn() { echo "[setup] warn: $1" >&2; }
|
|
62
|
+
|
|
63
|
+
# version_lt <a> <b> — a が b より古いか。
|
|
64
|
+
# ★`sort -V` に頼らない(Mac の sort には無い版がある=ADR-019 で Mac も対象)。
|
|
65
|
+
# 数字3つ(major.minor.patch)だけを見る。読めない字は 0 とみなす(安全側=古い扱い)。
|
|
66
|
+
version_lt() {
|
|
67
|
+
local a="${1%%-*}" b="${2%%-*}" i x y
|
|
68
|
+
local -a A B
|
|
69
|
+
IFS='.' read -r -a A <<< "$a"
|
|
70
|
+
IFS='.' read -r -a B <<< "$b"
|
|
71
|
+
for i in 0 1 2; do
|
|
72
|
+
x="${A[i]:-0}"; y="${B[i]:-0}"
|
|
73
|
+
[[ "$x" =~ ^[0-9]+$ ]] || x=0
|
|
74
|
+
[[ "$y" =~ ^[0-9]+$ ]] || y=0
|
|
75
|
+
if (( x < y )); then return 0; fi
|
|
76
|
+
if (( x > y )); then return 1; fi
|
|
77
|
+
done
|
|
78
|
+
return 1
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# いま入っている conductor の版(取れなければ空)。
|
|
82
|
+
conductor_version() {
|
|
83
|
+
local v
|
|
84
|
+
v="$(conductor --version 2>/dev/null | tr -d '[:space:]')"
|
|
85
|
+
if [[ "$v" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
|
86
|
+
printf '%s' "${BASH_REMATCH[0]}"
|
|
87
|
+
fi
|
|
88
|
+
}
|
|
45
89
|
step() { echo; echo "══════════════════════════════════════════════════════════"; echo "[setup] $1"; echo "══════════════════════════════════════════════════════════"; }
|
|
46
90
|
|
|
91
|
+
# ─── OS の見分けと、OS ごとに違う道具(★ADR-019・案C) ───────────────────────
|
|
92
|
+
# 【考え方】共通部分(前提チェック・番人の配置・鍵・自己点検)は【1本のまま】。
|
|
93
|
+
# OS で違うのは「常駐の仕組み」だけなので、そこだけ systemd 用 / launchd 用に分ける。
|
|
94
|
+
# ★2本のインストーラを作らない。作ると「直すたび2か所」になり、片方が古くなる事故が起きる
|
|
95
|
+
# (この製品で ADR-011/013 と繰り返し起きた型)。
|
|
96
|
+
case "$(uname -s)" in
|
|
97
|
+
Darwin) OS_KIND=mac ;;
|
|
98
|
+
*) OS_KIND=linux ;;
|
|
99
|
+
esac
|
|
100
|
+
# 常駐の呼び名(案内文で使う)。Mac は launchd、Linux(WSL含む) は systemd。
|
|
101
|
+
if [[ "$OS_KIND" == "mac" ]]; then
|
|
102
|
+
SVC_KIND="launchd"
|
|
103
|
+
PKG_HINT="brew install"
|
|
104
|
+
else
|
|
105
|
+
SVC_KIND="systemd"
|
|
106
|
+
PKG_HINT="sudo apt install"
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# ★bash 3.2(Mac 標準)でも動く小文字化。`${変数,,}` は bash 4 以降でしか使えない。
|
|
110
|
+
lower() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]'; }
|
|
111
|
+
|
|
112
|
+
# ★BSD でも GNU でも同じに動く「実体のパス」。`readlink -f` は BSD に無い版がある。
|
|
113
|
+
# python3 は①で必須にしているので、それに任せるのがいちばん確実。
|
|
114
|
+
resolve_path() {
|
|
115
|
+
python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$1" 2>/dev/null || printf '%s' "$1"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# ★BSD でも GNU でも同じに動く置換。`sed -i` は BSD だと引数が要る(`sed -i ''`)ので使わない。
|
|
119
|
+
sed_replace() { # sed_replace <置換式> <ファイル>
|
|
120
|
+
local expr="$1" file="$2" tmp="$2.setuptmp"
|
|
121
|
+
sed "$expr" "$file" > "$tmp" && mv -f "$tmp" "$file"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# ★常駐の生死を OS ごとに確かめる(自己点検⑦で使う)。
|
|
125
|
+
service_is_running() { # service_is_running <systemd のユニット名> <launchd のラベル>
|
|
126
|
+
if [[ "$OS_KIND" == "mac" ]]; then
|
|
127
|
+
launchctl list 2>/dev/null | grep -q "[[:space:]]$2\$"
|
|
128
|
+
else
|
|
129
|
+
systemctl --user is-active --quiet "$1"
|
|
130
|
+
fi
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# ★お客さまへ見せる「困ったときの1行」も OS で違う。案内文の中で使う。
|
|
134
|
+
if [[ "$OS_KIND" == "mac" ]]; then
|
|
135
|
+
SVC_LABEL="com.tyhld.conductor.${SITE}"
|
|
136
|
+
SVC_STATUS_CMD="launchctl list | grep ${SVC_LABEL}"
|
|
137
|
+
SVC_LOG_CMD="tail -f ~/Library/Logs/tyhld/conductor-${SITE}.log"
|
|
138
|
+
SVC_RESTART_CMD="launchctl kickstart -k gui/\$(id -u)/${SVC_LABEL}"
|
|
139
|
+
SVC_STOP_CMD="launchctl bootout gui/\$(id -u)/${SVC_LABEL}"
|
|
140
|
+
else
|
|
141
|
+
SVC_STATUS_CMD="systemctl --user status conductor@${SITE}"
|
|
142
|
+
SVC_LOG_CMD="journalctl --user -u conductor@${SITE} -f"
|
|
143
|
+
SVC_RESTART_CMD="systemctl --user restart conductor@${SITE}"
|
|
144
|
+
SVC_STOP_CMD="systemctl --user disable --now conductor@${SITE}"
|
|
145
|
+
fi
|
|
146
|
+
SVC_RESTART_HINT="${SVC_RESTART_CMD} を実行してください"
|
|
147
|
+
|
|
47
148
|
# ─── ① 前提チェック ──────────────────────────────────────────────────────────
|
|
48
149
|
step "① 前提チェック"
|
|
49
150
|
|
|
50
151
|
errors=()
|
|
51
152
|
|
|
52
|
-
# node >= 18
|
|
153
|
+
# node >= 18(実行要件。package.json の engines.node と一致させる)
|
|
154
|
+
# 顧客が実行するのは tsc でコンパイル済みの dist/ の JS なので、Node 18 で動く。
|
|
155
|
+
# Node 22 が要るのは開発時の `npm test` だけ(--experimental-strip-types で .ts を直接実行するため)。
|
|
156
|
+
# 開発要件はこのチェックではなく .nvmrc と CI の node-version で表明する。
|
|
157
|
+
# ここを 22 に上げると、実際には動く Node 18/20 の顧客をインストーラが弾いてしまう。
|
|
53
158
|
if command -v node >/dev/null 2>&1; then
|
|
54
159
|
NODE_VER="$(node --version 2>/dev/null || echo 'v0')"
|
|
55
160
|
NODE_MAJOR="${NODE_VER#v}"
|
|
@@ -60,21 +165,21 @@ if command -v node >/dev/null 2>&1; then
|
|
|
60
165
|
echo "[setup] node: $NODE_VER OK"
|
|
61
166
|
fi
|
|
62
167
|
else
|
|
63
|
-
errors+=("node が見つかりません。Node.js 18 以上をインストールしてください(
|
|
168
|
+
errors+=("node が見つかりません。Node.js 18 以上をインストールしてください(nvm / fnm / volta、または $PKG_HINT node)。")
|
|
64
169
|
fi
|
|
65
170
|
|
|
66
171
|
# tmux
|
|
67
172
|
if command -v tmux >/dev/null 2>&1; then
|
|
68
173
|
echo "[setup] tmux: $(tmux -V 2>/dev/null || echo 'OK')"
|
|
69
174
|
else
|
|
70
|
-
errors+=("tmux
|
|
175
|
+
errors+=("tmux が見つかりません。$PKG_HINT tmux でインストールしてください。")
|
|
71
176
|
fi
|
|
72
177
|
|
|
73
178
|
# git
|
|
74
179
|
if command -v git >/dev/null 2>&1; then
|
|
75
180
|
echo "[setup] git: $(git --version 2>/dev/null)"
|
|
76
181
|
else
|
|
77
|
-
errors+=("git
|
|
182
|
+
errors+=("git が見つかりません。$PKG_HINT git でインストールしてください。")
|
|
78
183
|
fi
|
|
79
184
|
|
|
80
185
|
# python3 >= 3.8
|
|
@@ -88,27 +193,58 @@ if command -v python3 >/dev/null 2>&1; then
|
|
|
88
193
|
echo "[setup] python3: $PY_VER OK"
|
|
89
194
|
fi
|
|
90
195
|
else
|
|
91
|
-
errors+=("python3
|
|
196
|
+
errors+=("python3 が見つかりません。$PKG_HINT python3 でインストールしてください。")
|
|
92
197
|
fi
|
|
93
198
|
|
|
94
|
-
# systemd
|
|
95
|
-
if
|
|
96
|
-
if
|
|
97
|
-
echo "[setup]
|
|
199
|
+
# 常駐の仕組み(★OS で見るものが違う。Mac に systemd は無い=ADR-019)
|
|
200
|
+
if [[ "$OS_KIND" == "mac" ]]; then
|
|
201
|
+
if command -v launchctl >/dev/null 2>&1; then
|
|
202
|
+
echo "[setup] launchd: OK(Mac の常駐の仕組み)"
|
|
98
203
|
else
|
|
99
|
-
errors+=("
|
|
204
|
+
errors+=("launchctl が見つかりません。macOS が壊れている可能性があります。")
|
|
100
205
|
fi
|
|
101
206
|
else
|
|
102
|
-
|
|
207
|
+
if command -v systemctl >/dev/null 2>&1; then
|
|
208
|
+
if systemctl --user show-environment >/dev/null 2>&1; then
|
|
209
|
+
echo "[setup] systemd: ユーザーセッション OK"
|
|
210
|
+
else
|
|
211
|
+
errors+=("systemd ユーザーセッションに接続できません。WSL の場合: /etc/wsl.conf に [boot] systemd=true を設定し wsl --shutdown で再起動してください。")
|
|
212
|
+
fi
|
|
213
|
+
else
|
|
214
|
+
errors+=("systemctl が見つかりません。systemd が有効か確認してください。")
|
|
215
|
+
fi
|
|
103
216
|
fi
|
|
104
217
|
|
|
105
|
-
# sudo
|
|
218
|
+
# sudo(★Mac では node のリンク作成をしないので必須にしない)
|
|
106
219
|
if command -v sudo >/dev/null 2>&1; then
|
|
107
220
|
echo "[setup] sudo: OK"
|
|
221
|
+
elif [[ "$OS_KIND" == "mac" ]]; then
|
|
222
|
+
echo "[setup] sudo: 見つかりませんが Mac では使いません(③は行いません)"
|
|
108
223
|
else
|
|
109
224
|
errors+=("sudo が見つかりません。node symlink の作成(③)で必要です。")
|
|
110
225
|
fi
|
|
111
226
|
|
|
227
|
+
# ★Claude Code(職人そのもの)
|
|
228
|
+
# 【なぜここで見るか】采配くんは「職人(Claude Code)へ指示を配る」仕組みなので、職人が
|
|
229
|
+
# 居なければ設置しても1つも動かない。ところが以前はチェックにも入れていなかったため、
|
|
230
|
+
# ⑦の案内どおり `claude` と打って初めて「コマンドが見つかりません」になり、
|
|
231
|
+
# お客さまには何が悪いのか分からなかった(調査 3f2337e6・2026-09-02)。
|
|
232
|
+
# 【★入れないと決めた理由】setup.sh は Claude Code を【入れない】。理由は3つ:
|
|
233
|
+
# 1. 入れてもログイン(ブラウザでのやり取り)が必ず人の手番として残る=手数は減らない。
|
|
234
|
+
# 2. お客さまのグローバル環境(npm -g)へ、采配くんの範囲を超えて手を入れることになる。
|
|
235
|
+
# 版を固定して運用しているお客さまの環境を壊しうる。
|
|
236
|
+
# 3. 「入れてあげる」より「無いことをはっきり伝えて止まる」ほうが、原因が分かる。
|
|
237
|
+
# =ここでは【確認して、無ければ入れ方を日本語で案内して中断する】に徹する。
|
|
238
|
+
if command -v claude >/dev/null 2>&1; then
|
|
239
|
+
echo "[setup] Claude Code: $(claude --version 2>/dev/null || echo 'OK')"
|
|
240
|
+
else
|
|
241
|
+
errors+=("Claude Code(職人)が見つかりません。采配くんは職人へ指示を配る仕組みなので、
|
|
242
|
+
先に職人を入れてログインしてください。次の2つを順に実行します:
|
|
243
|
+
npm i -g @anthropic-ai/claude-code
|
|
244
|
+
claude ← 画面の案内にそってログイン(1回だけ)
|
|
245
|
+
ログインが終わったら、この setup.sh をもう一度実行してください。")
|
|
246
|
+
fi
|
|
247
|
+
|
|
112
248
|
# プロジェクトディレクトリ
|
|
113
249
|
if [[ -d "$PROJECT_DIR" ]]; then
|
|
114
250
|
echo "[setup] プロジェクトディレクトリ: $PROJECT_DIR OK"
|
|
@@ -144,83 +280,385 @@ if ! command -v conductor >/dev/null 2>&1; then
|
|
|
144
280
|
fi
|
|
145
281
|
echo "[setup] conductor: $(conductor --version 2>/dev/null || command -v conductor)"
|
|
146
282
|
|
|
147
|
-
#
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
283
|
+
# --- ★版が古すぎないかを確かめる(古い版は動かない・実測 2026-09-04) --------
|
|
284
|
+
# 上で `npm update -g` は済んでいる。それでもまだ古いなら、取りに行った先に新しい版が
|
|
285
|
+
# 無い(=配布が止まっている)ということなので、ここで止めて人へ渡す。
|
|
286
|
+
# ★このまま進めると「設置は成功したのに指示が1つも届かない」で終わる。
|
|
287
|
+
# お客さまがいちばん困る形なので、必ずここで気づかせる(ADR-017 と同じ考え方)。
|
|
288
|
+
CONDUCTOR_VER="$(conductor_version)"
|
|
289
|
+
if [[ -z "$CONDUCTOR_VER" ]]; then
|
|
290
|
+
warn "conductor の版を読み取れませんでした(続行します)。"
|
|
291
|
+
elif version_lt "$CONDUCTOR_VER" "$MIN_CONDUCTOR_VERSION"; then
|
|
292
|
+
fail "采配くん本体の版が古すぎます(いま ${CONDUCTOR_VER} / ${MIN_CONDUCTOR_VERSION} 以上が必要です)。
|
|
293
|
+
更新を試しましたが、新しい版を取ってこられませんでした。
|
|
294
|
+
★古い版のままでは、設置できても指示が1つも届きません(鍵の名前が違うため)。
|
|
295
|
+
次の1行をお試しのうえ、それでも直らないときはサポートへご連絡ください:
|
|
296
|
+
npm install -g @tyhld/conductor@latest"
|
|
297
|
+
else
|
|
298
|
+
echo "[setup] conductor の版: OK(${CONDUCTOR_VER} ≧ ${MIN_CONDUCTOR_VERSION})"
|
|
160
299
|
fi
|
|
161
300
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
301
|
+
# ─── ③ node symlink ──────────────────────────────────────────────────────────
|
|
302
|
+
step "③ node の場所を常駐から見えるようにする"
|
|
303
|
+
|
|
304
|
+
# 【なぜ要るか】常駐(systemd / launchd)はログインシェルの PATH を引き継がない。
|
|
305
|
+
# そのため node の場所を「決め打ちで見える形」にしておく必要がある。
|
|
306
|
+
# ★OS で解き方が違う(ADR-019):
|
|
307
|
+
# Linux(WSL) … 従来どおり /usr/local/bin/node へリンクを作る(sudo)
|
|
308
|
+
# Mac … ★リンクを作らない。launchd の plist に node の絶対パスを直接書くので不要。
|
|
309
|
+
# sudo も要らず、Apple Silicon(/opt/homebrew/bin)でもそのまま動く。
|
|
310
|
+
NODE_PATH="$(resolve_path "$(command -v node)")"
|
|
311
|
+
echo "[setup] node の実体: $NODE_PATH"
|
|
312
|
+
|
|
313
|
+
if [[ "$OS_KIND" == "mac" ]]; then
|
|
314
|
+
echo "[setup] Mac のため、リンクは作りません(常駐には上の絶対パスをそのまま書きます)"
|
|
315
|
+
else
|
|
316
|
+
SYMLINK_PATH="/usr/local/bin/node"
|
|
317
|
+
need_symlink=true
|
|
318
|
+
if [[ -f "$SYMLINK_PATH" || -L "$SYMLINK_PATH" ]]; then
|
|
319
|
+
existing="$(resolve_path "$SYMLINK_PATH")"
|
|
320
|
+
if [[ "$existing" == "$NODE_PATH" ]]; then
|
|
321
|
+
echo "[setup] $SYMLINK_PATH は既に正しいリンクです → スキップ"
|
|
322
|
+
need_symlink=false
|
|
323
|
+
fi
|
|
324
|
+
fi
|
|
325
|
+
if $need_symlink; then
|
|
326
|
+
echo "[setup] $SYMLINK_PATH → $NODE_PATH のシンボリックリンクを作成します(sudo)"
|
|
327
|
+
if [[ -w "$(dirname "$SYMLINK_PATH")" ]]; then
|
|
328
|
+
ln -sf "$NODE_PATH" "$SYMLINK_PATH"
|
|
329
|
+
else
|
|
330
|
+
sudo ln -sf "$NODE_PATH" "$SYMLINK_PATH"
|
|
331
|
+
fi
|
|
332
|
+
echo "[setup] リンク作成完了"
|
|
168
333
|
fi
|
|
169
|
-
echo "[setup] symlink 作成完了"
|
|
170
334
|
fi
|
|
171
335
|
|
|
172
336
|
# ─── ④ .conductor.env 作成 ───────────────────────────────────────────────────
|
|
173
337
|
step "④ .conductor.env(管制サーバ接続情報)"
|
|
174
338
|
|
|
339
|
+
# ★合言葉(トークン)が管制に通るかを、その場で1回だけ確かめる。
|
|
340
|
+
# 【なぜ要るか】以前は入力後に何も試さなかったため、打ち間違えても最後まで「成功」で終わり、
|
|
341
|
+
# お客さまは後日「指示が届かない」で初めて気づいた(調査 3f2337e6・2026-09-02)。
|
|
342
|
+
# 【やり方】番人(hook_datalink.ping)とまったく同じ確かめ方を使う=実在しないIDへ
|
|
343
|
+
# 認証つきで1回 GET するだけ。★カードは作らない・何も書き換えない(副作用ゼロ)。
|
|
344
|
+
# 返す言葉: ok / bad-token / bad-url / unreachable
|
|
345
|
+
check_console_connection() {
|
|
346
|
+
local url="$1" token="$2"
|
|
347
|
+
CONDUCTOR_URL="$url" CONDUCTOR_TOKEN="$token" python3 <<'PYEOF' 2>/dev/null || echo unreachable
|
|
348
|
+
import os, urllib.error, urllib.request
|
|
349
|
+
|
|
350
|
+
PING_ID = '00000000-0000-4000-8000-000000000000'
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class _NoRedirect(urllib.request.HTTPRedirectHandler):
|
|
354
|
+
"""折返し(サインイン画面へのリダイレクト)を成功と読み違えない。"""
|
|
355
|
+
def redirect_request(self, *a, **k):
|
|
356
|
+
return None
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
url = (os.environ.get('CONDUCTOR_URL') or '').rstrip('/')
|
|
360
|
+
token = os.environ.get('CONDUCTOR_TOKEN') or ''
|
|
361
|
+
req = urllib.request.Request(
|
|
362
|
+
f'{url}/api/conductor/hook-requests/{PING_ID}',
|
|
363
|
+
method='GET', headers={'Authorization': f'Bearer {token}'})
|
|
364
|
+
try:
|
|
365
|
+
with urllib.request.build_opener(_NoRedirect).open(req, timeout=10) as res:
|
|
366
|
+
code = getattr(res, 'status', 0) or 0
|
|
367
|
+
print('ok' if 200 <= code < 300 else 'bad-url')
|
|
368
|
+
except urllib.error.HTTPError as e:
|
|
369
|
+
# 404 = 届いて認証も通り「そのIDが無い」だけ=正常。401/403 = 合言葉が違う。
|
|
370
|
+
if e.code == 404 or 200 <= e.code < 300:
|
|
371
|
+
print('ok')
|
|
372
|
+
elif e.code in (401, 403):
|
|
373
|
+
print('bad-token')
|
|
374
|
+
elif 300 <= e.code < 400:
|
|
375
|
+
print('bad-url')
|
|
376
|
+
else:
|
|
377
|
+
print('unreachable') # 5xx は管制側の一時的な不調とみなす
|
|
378
|
+
except Exception:
|
|
379
|
+
print('unreachable') # 名前が引けない・つながらない・時間切れ
|
|
380
|
+
PYEOF
|
|
381
|
+
}
|
|
382
|
+
|
|
175
383
|
ENV_FILE="$HOME/.conductor.env"
|
|
176
384
|
if [[ -f "$ENV_FILE" ]]; then
|
|
177
|
-
echo "[setup] $ENV_FILE は既に存在します →
|
|
178
|
-
echo "[setup]
|
|
385
|
+
echo "[setup] $ENV_FILE は既に存在します → スキップ(入力は求めません)"
|
|
386
|
+
echo "[setup] ★合言葉(トークン)を入れ直したいときは、次の1行でこのファイルを消してから"
|
|
387
|
+
echo "[setup] もう一度この setup.sh を実行してください:"
|
|
388
|
+
echo "[setup] rm ~/.conductor.env"
|
|
179
389
|
else
|
|
180
390
|
echo "[setup] 管制サーバへの接続情報を入力してください。"
|
|
391
|
+
echo "[setup] ★この2つは弊社からお渡しした値です。手元の案内をご覧ください。"
|
|
181
392
|
echo
|
|
182
393
|
|
|
183
|
-
read -r -p " CONDUCTOR_URL
|
|
184
|
-
[[ -z "$input_url" ]] && fail "
|
|
394
|
+
read -r -p " ① 管制のアドレス(CONDUCTOR_URL・例: https://…): " input_url
|
|
395
|
+
[[ -z "$input_url" ]] && fail "管制のアドレスが空です。"
|
|
185
396
|
|
|
186
|
-
|
|
187
|
-
|
|
397
|
+
# ★合言葉は画面に出さない(-s)。画面共有・録画に残さないため。
|
|
398
|
+
# 何も出ないと固まったと思われるので、受け取ったことを必ず1行返す。
|
|
399
|
+
read -r -s -p " ② 合言葉(CONDUCTOR_TOKEN・入力しても画面には出ません): " input_secret
|
|
400
|
+
echo
|
|
401
|
+
[[ -z "$input_secret" ]] && fail "合言葉が空です。"
|
|
402
|
+
echo "[setup] 受け取りました(${#input_secret} 文字)。画面には表示していません。"
|
|
403
|
+
|
|
404
|
+
# ★その場で1回だけ確かめる(副作用ゼロ)。
|
|
405
|
+
echo "[setup] 管制につながるか確認しています..."
|
|
406
|
+
case "$(check_console_connection "$input_url" "$input_secret")" in
|
|
407
|
+
ok)
|
|
408
|
+
echo "[setup] ✓ つながりました(アドレスと合言葉は正しいです)"
|
|
409
|
+
;;
|
|
410
|
+
bad-token)
|
|
411
|
+
fail "合言葉(トークン)が違うようです(管制に拒否されました)。
|
|
412
|
+
お手元の案内をもう一度ご確認のうえ、この setup.sh を実行し直してください。
|
|
413
|
+
★ここで止めるのは、このまま進めても指示が1つも届かないためです。"
|
|
414
|
+
;;
|
|
415
|
+
bad-url)
|
|
416
|
+
fail "管制のアドレスが違うようです(受け口に届いていません)。
|
|
417
|
+
https:// から始まる正しいアドレスか、末尾に余計な文字が入っていないかをご確認のうえ、
|
|
418
|
+
この setup.sh を実行し直してください。"
|
|
419
|
+
;;
|
|
420
|
+
*)
|
|
421
|
+
# ★一時的なネット不調と区別する。設置自体は続ける(後から直せる)。
|
|
422
|
+
warn "いま管制につながりませんでした(ネットワークが一時的に不調な可能性があります)。
|
|
423
|
+
★入力した値は保存し、設置は続けます。設置後に指示が届かないときは、
|
|
424
|
+
インターネット接続を確認してから次の1行で確かめ直してください:
|
|
425
|
+
${SVC_RESTART_CMD}"
|
|
426
|
+
;;
|
|
427
|
+
esac
|
|
188
428
|
|
|
189
429
|
cat > "$ENV_FILE" <<ENVEOF
|
|
190
430
|
CONDUCTOR_URL=${input_url}
|
|
191
|
-
|
|
431
|
+
CONDUCTOR_TOKEN=${input_secret}
|
|
192
432
|
ENVEOF
|
|
193
433
|
chmod 600 "$ENV_FILE"
|
|
194
|
-
echo "[setup] $ENV_FILE
|
|
434
|
+
echo "[setup] $ENV_FILE を作成しました(本人だけが読める権限にしました)"
|
|
195
435
|
fi
|
|
196
436
|
|
|
197
|
-
# ─── ⑤
|
|
198
|
-
step "⑤
|
|
437
|
+
# ─── ⑤ 番人一式の配置(ホーム1本方式・登録するイベントは見本が決める) ──────
|
|
438
|
+
step "⑤ 番人一式の配置(安全装置)"
|
|
199
439
|
|
|
200
440
|
CLAUDE_DIR="$PROJECT_DIR/.claude"
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
#
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
441
|
+
mkdir -p "$CLAUDE_DIR"
|
|
442
|
+
|
|
443
|
+
# ★番人は「1台に1箇所」だけ置く(現場ごとのコピーを作らない)。
|
|
444
|
+
# 以前は現場ごとに .claude/hooks/ へコピーしていたため、現場が増えるほど番人が増殖し、
|
|
445
|
+
# 更新のたびに全現場を回る必要があった(=取り残しが起きる)。集約すれば更新は1箇所で済む。
|
|
446
|
+
# ★番人フックは【ホームの ~/.claude/settings.json に1本だけ】書く。各現場の
|
|
447
|
+
# PROJECT_DIR/.claude/settings.json には番人フックを書かない(=二重番人を作らない・ADR-013)。
|
|
448
|
+
# ★どのイベントを登録するかは【見本 scripts/hooks/settings.sample.json だけ】が決める
|
|
449
|
+
# (ADR-005 と同じ考え方)。以前はここにイベント名を直書きしていたため、ADR-016 で
|
|
450
|
+
# Notification を足しても【新規に設置したお客さまの機体には入らなかった】。
|
|
451
|
+
# 下の ⑤-0 で見本と配る中身を機械照合し、外れていれば設置を止める=足し忘れが起こせない。
|
|
452
|
+
GUARD_HOME="$HOME/.tyhld/hooks"
|
|
453
|
+
GUARD_SAMPLE="$SCRIPT_DIR/../scripts/hooks/settings.sample.json"
|
|
454
|
+
|
|
455
|
+
# --- 番人一式(正本1本)を固定パスへ配置 ---
|
|
456
|
+
# ★配布元は「正本1本」だけ。以前は sales-template/ に複製を置いていたため、正本が更新されても
|
|
457
|
+
# 顧客へ届かず乖離した。複製は廃止し、ここでは常に正本を配布する=乖離が構造的に起きない。
|
|
458
|
+
#
|
|
459
|
+
# 正本の在り処は「setup.sh から見て ../scripts/hooks」。npm 配布物でもリポジトリでも
|
|
460
|
+
# 同じ相対位置になる(package.json の files に scripts/hooks を含めてある)。
|
|
461
|
+
GUARD_SRC_DIR="$SCRIPT_DIR/../scripts/hooks"
|
|
462
|
+
|
|
463
|
+
# ★配る番人一式(段2の3フック+その依存)。scripts/update-guard.sh と同じ集合にする。
|
|
464
|
+
# 1本でも欠けると差し替えた現場の番人が読めず止まる(勤務用コピーの考え方=ADR-002)。
|
|
465
|
+
# ★この一覧は scripts/update-guard.sh の INSTALL_FILES と【同じ集合】にすること。
|
|
466
|
+
# 食い違うと「稼働機では動くのに新規設置だけ壊れる」が起きる。テストで同一を固定してある。
|
|
467
|
+
GUARD_FILES=(
|
|
468
|
+
cc2_guard.py # PreToolUse … 危険を止める/関門を人へ渡す
|
|
469
|
+
permission_request_hook.py # PermissionRequest … 確認を管制へ送り答えを待つ
|
|
470
|
+
stop_hook.py # Stop … 手が止まったことを管制へ知らせる
|
|
471
|
+
notification_hook.py # Notification … 端末でしか押せない問いを管制へ知らせる(ADR-016)
|
|
472
|
+
policy.py # cc2_guard / permission_request_hook が使う判定本体
|
|
473
|
+
summary_ja.py # permission_request_hook / stop_hook が使う日本語の文言
|
|
474
|
+
hook_datalink.py # 各フックが使う管制との連絡線
|
|
475
|
+
)
|
|
476
|
+
GUARD_ENTRYPOINTS=(cc2_guard.py permission_request_hook.py stop_hook.py notification_hook.py)
|
|
477
|
+
GUARD_CONFIGS=(safe_verbs.json network_allow.json) # 任意・あれば同居させる
|
|
478
|
+
|
|
479
|
+
# --- ⑤-0 見本と「配る中身」がズレていないか機械照合する(★足し忘れの再発防止) ---
|
|
480
|
+
# 見本が呼ぶ入口スクリプトが (a) 正本にあるか (b) 上の GUARD_FILES に入っているか を確かめる。
|
|
481
|
+
# 1つでも外れていれば「登録はされるのに実体が無い」=番人が動かない状態になるので、ここで止める。
|
|
482
|
+
# ★見本が無い環境では止めない(rc=7)。従来どおりの3フックで進む=壊さない。
|
|
483
|
+
echo "[setup] ⑤-0 見本と配る中身の照合"
|
|
484
|
+
drift_out="$(GUARD_FILES="${GUARD_FILES[*]}" python3 - "$GUARD_SAMPLE" "$GUARD_SRC_DIR" <<'PYEOF'
|
|
485
|
+
import json, os, sys
|
|
486
|
+
|
|
487
|
+
sample_path, src_dir = sys.argv[1], sys.argv[2]
|
|
488
|
+
install = set(os.environ.get("GUARD_FILES", "").split())
|
|
489
|
+
|
|
490
|
+
try:
|
|
491
|
+
hooks = (json.load(open(sample_path, encoding="utf-8")).get("hooks") or {})
|
|
492
|
+
except FileNotFoundError:
|
|
493
|
+
print("SKIP 見本がありません(従来どおりの内容で設置します)")
|
|
494
|
+
sys.exit(7)
|
|
495
|
+
except Exception as e: # noqa: BLE001
|
|
496
|
+
print("FAIL 見本が JSON として読めません: %s" % e)
|
|
497
|
+
sys.exit(1)
|
|
498
|
+
if not hooks:
|
|
499
|
+
print("FAIL 見本に hooks がありません(配る対象が決められない)")
|
|
500
|
+
sys.exit(1)
|
|
501
|
+
|
|
502
|
+
refs = {os.path.basename(h["command"].split()[-1])
|
|
503
|
+
for groups in hooks.values() for g in (groups or [])
|
|
504
|
+
for h in (g.get("hooks") or []) if h.get("command")}
|
|
505
|
+
missing_src = sorted(b for b in refs if not os.path.isfile(os.path.join(src_dir, b)))
|
|
506
|
+
not_installed = sorted(b for b in refs if b not in install)
|
|
507
|
+
if missing_src:
|
|
508
|
+
print("FAIL 見本が呼ぶスクリプトが正本にありません: %s" % ", ".join(missing_src))
|
|
509
|
+
sys.exit(1)
|
|
510
|
+
if not_installed:
|
|
511
|
+
print("FAIL 見本が呼ぶスクリプトが設置一覧(GUARD_FILES)にありません: %s"
|
|
512
|
+
% ", ".join(not_installed))
|
|
513
|
+
sys.exit(1)
|
|
514
|
+
print("OK 見本の %d イベント(%s)を登録します" % (len(hooks), ", ".join(sorted(hooks))))
|
|
515
|
+
PYEOF
|
|
516
|
+
)" && drift_rc=0 || drift_rc=$?
|
|
517
|
+
echo "[setup] $drift_out"
|
|
518
|
+
if [[ "${drift_rc:-1}" == "1" ]]; then
|
|
519
|
+
fail "見本と配る中身が食い違っています(上記)。配布物が壊れている可能性があります。
|
|
520
|
+
お手数ですが、この画面のままサポートへご連絡ください。"
|
|
521
|
+
fi
|
|
522
|
+
SAMPLE_OK=0
|
|
523
|
+
[[ "${drift_rc:-1}" == "0" ]] && SAMPLE_OK=1
|
|
524
|
+
|
|
525
|
+
# ★番人は安全境界そのもの。見つからないまま黙って進むと「守りが無い状態」で稼働してしまう。
|
|
526
|
+
# 従来は warn+スキップだったが、それは最も危険な失敗の仕方なので中断する。
|
|
527
|
+
missing_guard=()
|
|
528
|
+
for f in "${GUARD_FILES[@]}"; do
|
|
529
|
+
[[ -f "$GUARD_SRC_DIR/$f" ]] || missing_guard+=("$f")
|
|
530
|
+
done
|
|
531
|
+
if [[ ${#missing_guard[@]} -gt 0 ]]; then
|
|
532
|
+
fail "番人一式が見つかりません: ${missing_guard[*]}(探索先: $GUARD_SRC_DIR)
|
|
533
|
+
安全装置が揃わない状態では設置できません。配布物が壊れている可能性があります。
|
|
534
|
+
お手数ですが、この画面のままサポートへご連絡ください。"
|
|
535
|
+
fi
|
|
536
|
+
|
|
537
|
+
# ★壊れた Python を固定パスへ置くと全現場が同時に止まる。置く前に構文を確認する。
|
|
538
|
+
if ! python3 -m py_compile "${GUARD_FILES[@]/#/$GUARD_SRC_DIR/}"; then
|
|
539
|
+
fail "番人一式の構文確認に失敗しました。固定パスは変更していません。"
|
|
540
|
+
fi
|
|
541
|
+
|
|
542
|
+
mkdir -p "$GUARD_HOME"
|
|
543
|
+
for f in "${GUARD_FILES[@]}"; do
|
|
544
|
+
cp -f "$GUARD_SRC_DIR/$f" "$GUARD_HOME/$f"
|
|
545
|
+
done
|
|
546
|
+
for e in "${GUARD_ENTRYPOINTS[@]}"; do
|
|
547
|
+
chmod +x "$GUARD_HOME/$e"
|
|
548
|
+
done
|
|
549
|
+
echo "[setup] 番人一式(${#GUARD_FILES[@]}本)を配置: $GUARD_HOME(この1台の全現場が共有します)"
|
|
550
|
+
|
|
551
|
+
# --- 番人の設定ファイル(任意・同居必須)---
|
|
552
|
+
# 番人は「自分と同じフォルダ」の safe_verbs.json / network_allow.json を読む(_SCRIPT_DIR 基準)。
|
|
553
|
+
# 無い場合は番人が既定値で動く(フェイルセーフ)ので、存在するものだけ配置する。
|
|
554
|
+
for guard_conf in "${GUARD_CONFIGS[@]}"; do
|
|
555
|
+
if [[ -f "$GUARD_SRC_DIR/$guard_conf" ]]; then
|
|
556
|
+
cp -f "$GUARD_SRC_DIR/$guard_conf" "$GUARD_HOME/$guard_conf"
|
|
557
|
+
echo "[setup] 配置: $GUARD_HOME/$guard_conf"
|
|
558
|
+
fi
|
|
559
|
+
done
|
|
560
|
+
|
|
561
|
+
# --- 番人フックを【ホームの ~/.claude/settings.json】へ1本だけ登録 ---
|
|
562
|
+
# ★1台1箇所。ここに書けば、この機の全現場(全 tmux セッション)へ同時に効く。
|
|
563
|
+
# 現場ごとの settings.json には番人フックを書かない=二重番人・取り残しを構造的に防ぐ(ADR-013)。
|
|
564
|
+
# ★登録するイベントは【見本 settings.sample.json が正】。ここに名前を直書きしない。
|
|
565
|
+
# 直書きしていたせいで、ADR-016 で Notification を足しても新規設置には入らなかった。
|
|
566
|
+
# 見本が無い環境だけ、従来どおりの3イベントへ落ちる(後退させない)。
|
|
567
|
+
# ★matcher は付けない(PreToolUse は全ツール対象)。番人は Bash 以外も見るため、列挙すると
|
|
568
|
+
# 新しいツールが増えたとき素通りする(見本の方針に一致)。
|
|
569
|
+
# ★command には ~ を使わず絶対パスを書く。フックがシェル経由で実行される保証が無く、
|
|
570
|
+
# ~ が展開されないと番人が起動せず「守りが黙って外れる」最悪の失敗になるため。
|
|
571
|
+
# ★既存の ~/.claude/settings.json の他キー(permissions 等)は壊さず hooks だけ差し替える。
|
|
572
|
+
HOME_CLAUDE_DIR="$HOME/.claude"
|
|
573
|
+
HOME_SETTINGS="$HOME_CLAUDE_DIR/settings.json"
|
|
574
|
+
mkdir -p "$HOME_CLAUDE_DIR"
|
|
575
|
+
registered_events="$(GUARD_HOME="$GUARD_HOME" HOME_SETTINGS="$HOME_SETTINGS" \
|
|
576
|
+
SAMPLE_OK="$SAMPLE_OK" python3 - "$GUARD_SAMPLE" <<'PYEOF'
|
|
577
|
+
import json, os, shutil, sys, time
|
|
578
|
+
|
|
579
|
+
p = os.environ['HOME_SETTINGS']
|
|
580
|
+
gh = os.environ['GUARD_HOME']
|
|
581
|
+
sample_ok = os.environ.get('SAMPLE_OK') == '1'
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def at(name):
|
|
585
|
+
return 'python3 ' + os.path.join(gh, name)
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def from_sample(path):
|
|
589
|
+
"""見本の hooks を、コマンドのパスだけ固定パスへ置き換えた形で返す。"""
|
|
590
|
+
hooks = json.load(open(path, encoding='utf-8')).get('hooks') or {}
|
|
591
|
+
out = {}
|
|
592
|
+
for ev, groups in hooks.items():
|
|
593
|
+
new = []
|
|
594
|
+
for g in (groups or []):
|
|
595
|
+
ng = dict(g)
|
|
596
|
+
ng['hooks'] = [
|
|
597
|
+
{**h, 'command': at(os.path.basename(h['command'].split()[-1]))}
|
|
598
|
+
if isinstance(h, dict) and h.get('command') else h
|
|
599
|
+
for h in (g.get('hooks') or [])
|
|
600
|
+
]
|
|
601
|
+
new.append(ng)
|
|
602
|
+
out[ev] = new
|
|
603
|
+
return out
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
if sample_ok:
|
|
607
|
+
desired = from_sample(sys.argv[1])
|
|
608
|
+
else:
|
|
609
|
+
# 見本が無い環境(配布物が古い等)。従来どおりの3イベントで設置する=後退させない。
|
|
610
|
+
desired = {
|
|
611
|
+
'PreToolUse': [{'hooks': [{'type': 'command', 'command': at('cc2_guard.py')}]}],
|
|
612
|
+
'PermissionRequest': [{'hooks': [
|
|
613
|
+
{'type': 'command', 'timeout': 600, 'command': at('permission_request_hook.py')}]}],
|
|
614
|
+
'Stop': [{'hooks': [
|
|
615
|
+
{'type': 'command', 'timeout': 600, 'command': at('stop_hook.py')}]}],
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
d = {}
|
|
619
|
+
if os.path.exists(p):
|
|
620
|
+
with open(p, encoding='utf-8') as f:
|
|
621
|
+
d = json.load(f) # 壊れていれば例外→インストーラが警告して手動対応を促す
|
|
622
|
+
if not isinstance(d, dict):
|
|
623
|
+
d = {}
|
|
624
|
+
|
|
625
|
+
d['hooks'] = desired
|
|
626
|
+
|
|
627
|
+
if os.path.exists(p):
|
|
628
|
+
shutil.copy2(p, p + '.bak.' + time.strftime('%Y%m%d-%H%M%S'))
|
|
629
|
+
tmp = p + '.tmp'
|
|
630
|
+
with open(tmp, 'w', encoding='utf-8') as f:
|
|
631
|
+
json.dump(d, f, indent=2, ensure_ascii=False)
|
|
632
|
+
f.write('\n')
|
|
633
|
+
os.replace(tmp, p) # 置換は原子的=途中で壊れた settings.json を残さない
|
|
634
|
+
print(' / '.join(sorted(desired)))
|
|
635
|
+
PYEOF
|
|
636
|
+
)" || fail "番人フックの登録に失敗しました: $HOME_SETTINGS
|
|
637
|
+
既存ファイルが JSON として壊れている可能性があります。手動で確認してください。"
|
|
638
|
+
echo "[setup] 番人フック($registered_events)を $HOME_SETTINGS に登録しました"
|
|
639
|
+
|
|
640
|
+
# --- Codex の職人にも番人を登録する(★ADR-022・Codex を使う機体だけ) -------
|
|
641
|
+
# ★同じ処理を2か所に書かない: 稼働機の更新(update-guard.sh ①-e2)と同じスクリプトを呼ぶ。
|
|
642
|
+
# Codex を使っていない機体では中で何もしない(~/.codex を勝手に作らない)。
|
|
643
|
+
CODEX_HOOK_INSTALL="$SCRIPT_DIR/../scripts/hooks/install-codex-hooks.sh"
|
|
644
|
+
if [[ -f "$CODEX_HOOK_INSTALL" ]]; then
|
|
645
|
+
APPLY=1 GUARD_HOME="$GUARD_HOME" bash "$CODEX_HOOK_INSTALL" \
|
|
646
|
+
|| warn "Codex への番人登録に失敗しました(Claude 側の設置は完了しています)"
|
|
211
647
|
else
|
|
212
|
-
warn "
|
|
648
|
+
warn "Codex 用の登録スクリプトが見つかりません: $CODEX_HOOK_INSTALL(スキップ)"
|
|
213
649
|
fi
|
|
214
650
|
|
|
215
|
-
# --- settings.json ---
|
|
651
|
+
# --- 現場の権限ルール settings.json(番人フックは書かない) ---
|
|
652
|
+
# ★ここに置くのは「その現場固有の権限ルール(deny・Supabase 等の sandbox)」だけ。
|
|
653
|
+
# 番人フックは上でホームに1本化済み。現場側には hooks を書かない(二重番人を作らない)。
|
|
216
654
|
SETTINGS_SRC="$SCRIPT_DIR/settings.json"
|
|
217
655
|
SETTINGS_DST="$CLAUDE_DIR/settings.json"
|
|
218
656
|
if [[ -f "$SETTINGS_SRC" ]]; then
|
|
219
657
|
if [[ -f "$SETTINGS_DST" ]]; then
|
|
220
658
|
echo "[setup] $SETTINGS_DST は既に存在します。上書きしますか?"
|
|
221
659
|
read -r -p " 上書き (y/N): " overwrite
|
|
222
|
-
if [[ "$
|
|
223
|
-
echo "[setup] settings.json をスキップしました。"
|
|
660
|
+
if [[ "$(lower "$overwrite")" != "y" ]]; then
|
|
661
|
+
echo "[setup] 現場の settings.json をスキップしました。"
|
|
224
662
|
else
|
|
225
663
|
cp -f "$SETTINGS_SRC" "$SETTINGS_DST"
|
|
226
664
|
echo "[setup] 配置: $SETTINGS_DST(上書き)"
|
|
@@ -233,7 +671,7 @@ if [[ -f "$SETTINGS_SRC" ]]; then
|
|
|
233
671
|
# <PROJECT_DIR> をプロジェクトの絶対パスに置換
|
|
234
672
|
if [[ -f "$SETTINGS_DST" ]] && grep -q '<PROJECT_DIR>' "$SETTINGS_DST"; then
|
|
235
673
|
ABS_PROJECT_DIR="$(cd "$PROJECT_DIR" && pwd)"
|
|
236
|
-
|
|
674
|
+
sed_replace "s|<PROJECT_DIR>|${ABS_PROJECT_DIR}|g" "$SETTINGS_DST"
|
|
237
675
|
echo "[setup] <PROJECT_DIR> → $ABS_PROJECT_DIR に置換"
|
|
238
676
|
fi
|
|
239
677
|
|
|
@@ -241,10 +679,10 @@ if [[ -f "$SETTINGS_SRC" ]]; then
|
|
|
241
679
|
if [[ -f "$SETTINGS_DST" ]] && grep -q '<SUPABASE_PROJECT_ID>' "$SETTINGS_DST"; then
|
|
242
680
|
echo
|
|
243
681
|
read -r -p "[setup] Supabase を使いますか? (y/N): " use_supabase
|
|
244
|
-
if [[ "$
|
|
682
|
+
if [[ "$(lower "$use_supabase")" == "y" ]]; then
|
|
245
683
|
read -r -p " Supabase Project Reference ID: " supabase_id
|
|
246
684
|
if [[ -n "$supabase_id" ]]; then
|
|
247
|
-
|
|
685
|
+
sed_replace "s|<SUPABASE_PROJECT_ID>|${supabase_id}|g" "$SETTINGS_DST"
|
|
248
686
|
echo "[setup] <SUPABASE_PROJECT_ID> → $supabase_id に置換"
|
|
249
687
|
else
|
|
250
688
|
warn "Supabase ID が空のため、sandbox 設定にプレースホルダが残ります。"
|
|
@@ -272,79 +710,278 @@ else
|
|
|
272
710
|
warn "settings.json が見つかりません: $SETTINGS_SRC(スキップ)"
|
|
273
711
|
fi
|
|
274
712
|
|
|
275
|
-
# ---
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
"
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
'type': 'command',
|
|
296
|
-
'command': 'python3 .claude/hooks/cc2_guard.py'
|
|
297
|
-
}
|
|
298
|
-
]
|
|
299
|
-
}
|
|
300
|
-
]
|
|
301
|
-
}
|
|
302
|
-
with open('$SETTINGS_DST', 'w') as f:
|
|
303
|
-
json.dump(d, f, indent=2, ensure_ascii=False)
|
|
304
|
-
f.write('\n')
|
|
305
|
-
" 2>/dev/null && echo "[setup] hooks 設定を settings.json に注入しました" \
|
|
306
|
-
|| warn "hooks 設定の注入に失敗しました。手動で追加してください。"
|
|
307
|
-
else
|
|
308
|
-
echo "[setup] hooks 設定は既に存在します → スキップ"
|
|
309
|
-
fi
|
|
713
|
+
# --- 職人の鉄則スキル conductor-craftsman を配置 ------------------------------
|
|
714
|
+
# ★番人(安全装置)が「やらせない」側なら、こちらは職人自身が「そう動く」側。
|
|
715
|
+
# 便(CONDUCTOR_JOB 印の付いた作業指示)を受けた職人が最初に読む作業規律を、
|
|
716
|
+
# 番人と同じく「1台に1箇所」=ホームの ~/.claude/skills/ に置く。現場ごとに
|
|
717
|
+
# コピーを作らない(増殖させない・更新は1箇所で済む)。
|
|
718
|
+
# ★配布元は正本1本(リポジトリの skills/)。番人と同じく setup.sh からの相対位置で探す。
|
|
719
|
+
# ★番人と違い、これは安全境界ではなく作業規律なので、見つからないときは中断せず警告して進む
|
|
720
|
+
# (スキルが無くても番人は効いており、安全に穴は空かない)。
|
|
721
|
+
CRAFTSMAN_SKILL_NAME="conductor-craftsman"
|
|
722
|
+
CRAFTSMAN_SKILL_SRC="$SCRIPT_DIR/../skills/$CRAFTSMAN_SKILL_NAME"
|
|
723
|
+
CRAFTSMAN_SKILL_DST="$HOME/.claude/skills/$CRAFTSMAN_SKILL_NAME"
|
|
724
|
+
|
|
725
|
+
if [[ -f "$CRAFTSMAN_SKILL_SRC/SKILL.md" ]]; then
|
|
726
|
+
# 冪等: 毎回 mkdir -p + cp -f で正本の内容に揃える(再実行しても壊れない・差分だけ上書き)。
|
|
727
|
+
mkdir -p "$CRAFTSMAN_SKILL_DST"
|
|
728
|
+
cp -f "$CRAFTSMAN_SKILL_SRC/SKILL.md" "$CRAFTSMAN_SKILL_DST/SKILL.md"
|
|
729
|
+
echo "[setup] 職人の鉄則スキルを配置: $CRAFTSMAN_SKILL_DST(この1台の全現場が共有します)"
|
|
730
|
+
else
|
|
731
|
+
warn "職人の鉄則スキルが見つかりません: $CRAFTSMAN_SKILL_SRC/SKILL.md(スキップ)
|
|
732
|
+
番人は設置済みのため安全装置は効いていますが、職人の作業規律は配られていません。"
|
|
310
733
|
fi
|
|
311
734
|
|
|
312
735
|
# ─── ⑥ systemd install ──────────────────────────────────────────────────────
|
|
313
736
|
step "⑥ systemd 常駐サービスの設定"
|
|
314
737
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
738
|
+
# --- 古いユニットの掃除(best-effort・冪等) ---------------------------------
|
|
739
|
+
# アップグレード時、旧バージョンが入れたユニットは cp -f の上書きでは消えない。
|
|
740
|
+
# 特に `@` を含まない単発ユニット(旧 conductor-orchestrator.service)は誰も消さず、
|
|
741
|
+
# enabled のまま常駐し続ける(2026-07-09 実地。10日間 heartbeat を送り続けた)。
|
|
742
|
+
#
|
|
743
|
+
# ★掃除してよいのは「このインストーラが過去に置いたが、現行版ではもう置かないユニット」だけ。
|
|
744
|
+
# 現行の systemd/install.sh が置くのは conductor@.service(テンプレート本体)と
|
|
745
|
+
# conductor@<現場>.service の2種のみ。したがって掃除対象は、台帳に載っている
|
|
746
|
+
# `@` 無しの単発ユニット(conductor-*.service)に尽きる。
|
|
747
|
+
#
|
|
748
|
+
# ★conductor@* には一切触れない(テンプレート本体・各現場とも)。
|
|
749
|
+
# 台帳にはインストール済みの「全現場」が載る。今回の現場以外を掃除対象にすると、
|
|
750
|
+
# 顧客が2つ目の現場を追加するために setup.sh を再実行しただけで、1つ目の現場が
|
|
751
|
+
# disable --now されて消える。conductor@<現場> は常に現役の可能性がある。
|
|
752
|
+
#
|
|
753
|
+
# ★台帳が無い/空なら何もしない。setup.sh は無人で走りうるため、確認プロンプトも
|
|
754
|
+
# --dry-run も無いこの場所で、顧客自作の conductor-*.service を巻き込む可能性のある
|
|
755
|
+
# 走査を実行してはならない。台帳導入前の環境の掃除は uninstall.sh(--dry-run 付き)に委ねる。
|
|
756
|
+
#
|
|
757
|
+
# 冪等性の条件:
|
|
758
|
+
# - 対象が無くてもエラーで落ちない(|| true で握りつぶす)
|
|
759
|
+
# - disable --now → ファイル削除 → 最後に一度だけ daemon-reload の順序を守る
|
|
760
|
+
# - set -e 下でも掃除の失敗が本体インストールを中断しない(best-effort)
|
|
761
|
+
# - 2回連続実行すると2回目は no-op(1回目で台帳から消えるため)
|
|
762
|
+
#
|
|
763
|
+
# ★掃除の例外(現行版が入れる「@ 無し」のユニット)— ADR-066 便2e
|
|
764
|
+
# 耳 conductor-ear.service は「PCに1本」の現役ユニットで、`@` を含まないため、
|
|
765
|
+
# 何もしなければ上の掃除規則(台帳に載っている conductor-*.service)に丸ごと当たり、
|
|
766
|
+
# セットアップを再実行するたびに自分で入れた耳を自分で消してしまう。
|
|
767
|
+
# ★この例外は【台帳へ載せる処理より先】に入っていなければならない。順序を逆にすると、
|
|
768
|
+
# 一度入った耳が次回セットアップで撤去される(便2c の申し送りで警告した罠)。
|
|
769
|
+
# 耳を台帳へ載せるのは systemd/ear-install.sh で、下の cleanup_stale_units 呼び出しより
|
|
770
|
+
# 後に実行される。この順序は test/setup-ear-order.test.ts が固定している。
|
|
771
|
+
CURRENT_MACHINE_UNITS=(
|
|
772
|
+
conductor-ear.service # ADR-066 の耳(このPCに1本・現役)。掃除してはいけない。
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
cleanup_stale_units() {
|
|
776
|
+
local ledger_dir="${XDG_CONFIG_HOME:-$HOME/.config}/conductor"
|
|
777
|
+
local ledger_file="$ledger_dir/installed-units.txt"
|
|
778
|
+
local unit_dir="$HOME/.config/systemd/user"
|
|
779
|
+
local stale=() u raw line keep
|
|
780
|
+
local cleaned=0
|
|
781
|
+
|
|
782
|
+
# 台帳が無い=台帳導入前の環境。ここでは何も掃除しない(安全側・上記★参照)。
|
|
783
|
+
if [[ ! -f "$ledger_file" ]]; then
|
|
784
|
+
echo "[setup] 台帳が無いため古いユニットの掃除はスキップします。"
|
|
785
|
+
echo "[setup] 旧環境の撤去は 'bash uninstall.sh --dry-run' で対象を確認のうえ実施してください。"
|
|
786
|
+
return 0
|
|
787
|
+
fi
|
|
788
|
+
|
|
789
|
+
# 台帳に載っている `@` 無しの単発ユニットだけを掃除対象にする。
|
|
790
|
+
while IFS= read -r raw || [[ -n "$raw" ]]; do
|
|
791
|
+
line="${raw#"${raw%%[![:space:]]*}"}"
|
|
792
|
+
line="${line%"${line##*[![:space:]]}"}"
|
|
793
|
+
[[ -z "$line" || "${line:0:1}" == "#" ]] && continue
|
|
794
|
+
# conductor@... は現役の可能性があるので一切触らない(テンプレート本体・各現場とも)。
|
|
795
|
+
[[ "$line" == conductor@* ]] && continue
|
|
796
|
+
# ★現行版が入れる「@ 無し」の現役ユニット(耳など)は掃除しない。
|
|
797
|
+
for keep in "${CURRENT_MACHINE_UNITS[@]}"; do
|
|
798
|
+
[[ "$line" == "$keep" ]] && continue 2
|
|
799
|
+
done
|
|
800
|
+
# 掃除対象は「現行版がもう置かない」単発ユニットのみ。それ以外の綴りは無視する。
|
|
801
|
+
[[ "$line" == conductor-*.service ]] || continue
|
|
802
|
+
stale+=("$line")
|
|
803
|
+
done < "$ledger_file"
|
|
804
|
+
|
|
805
|
+
if [[ ${#stale[@]} -eq 0 ]]; then
|
|
806
|
+
return 0
|
|
807
|
+
fi
|
|
808
|
+
|
|
809
|
+
for u in "${stale[@]}"; do
|
|
810
|
+
systemctl --user disable --now "$u" >/dev/null 2>&1 || true
|
|
811
|
+
if [[ -f "$unit_dir/$u" ]]; then
|
|
812
|
+
rm -f "$unit_dir/$u" || true
|
|
813
|
+
fi
|
|
814
|
+
# 撤去したものは台帳からも消す(台帳に幽霊を残さない)。
|
|
815
|
+
grep -vxF "$u" "$ledger_file" > "$ledger_file.tmp" 2>/dev/null || true
|
|
816
|
+
mv -f "$ledger_file.tmp" "$ledger_file" 2>/dev/null || rm -f "$ledger_file.tmp"
|
|
817
|
+
echo "[setup] 古いユニットを撤去: $u"
|
|
818
|
+
cleaned=1
|
|
819
|
+
done
|
|
820
|
+
|
|
821
|
+
if [[ $cleaned -eq 1 ]]; then
|
|
822
|
+
systemctl --user daemon-reload || true
|
|
823
|
+
echo "[setup] daemon-reload 実行(掃除の反映)"
|
|
824
|
+
fi
|
|
825
|
+
return 0
|
|
826
|
+
}
|
|
827
|
+
# best-effort: 掃除が失敗しても本体インストールは続行する(warn は上で定義済み)。
|
|
828
|
+
# ★古いユニットの掃除は systemd の話。Mac には掃除対象が存在しないので行わない(ADR-019)。
|
|
829
|
+
if [[ "$OS_KIND" == "mac" ]]; then
|
|
830
|
+
echo "[setup] 古いユニットの掃除は Mac では不要のため行いません"
|
|
831
|
+
else
|
|
832
|
+
cleanup_stale_units || warn "古いユニットの掃除に失敗しました(インストールは続行します)。"
|
|
833
|
+
fi
|
|
834
|
+
|
|
835
|
+
# ★常駐の設置だけが OS で分かれる(ADR-019・案C)。ここより上も下も1本のまま。
|
|
836
|
+
if [[ "$OS_KIND" == "mac" ]]; then
|
|
837
|
+
SVC_INSTALL="$SCRIPT_DIR/launchd/install.sh"
|
|
838
|
+
else
|
|
839
|
+
SVC_INSTALL="$SCRIPT_DIR/systemd/install.sh"
|
|
840
|
+
fi
|
|
841
|
+
if [[ -f "$SVC_INSTALL" ]]; then
|
|
842
|
+
bash "$SVC_INSTALL" "$SITE" "$PROJECT_DIR"
|
|
843
|
+
else
|
|
844
|
+
warn "常駐の設置スクリプトが見つかりません: $SVC_INSTALL(スキップ)"
|
|
845
|
+
echo "[setup] 手動で設定する場合は sales-template/${SVC_KIND}/ を参照してください。"
|
|
846
|
+
fi
|
|
847
|
+
|
|
848
|
+
# --- 耳(指示の着信合図の受け取り役/このPCに1本)の設置 ---------------------
|
|
849
|
+
# ADR-066: 指示が積まれた合図を受け取るのは PC につき1本の conductor-ear だけ(現場ごとではない)。
|
|
850
|
+
# 耳が受けた合図は、その現場の relay へ「今すぐ1周して」と伝えるだけ。指示は relay が
|
|
851
|
+
# 従来どおり取得APIから取る。=つなぎっぱなしの本数がソフト数ではなく PC 数で決まる。
|
|
852
|
+
# ★導入処理の正本は配布物の systemd/ear-install.sh(自社機の導入処理と共用・写しを持たない)。
|
|
853
|
+
# ★best-effort: 耳が入らなくてもセットアップは成功扱いにする。
|
|
854
|
+
# 耳は加速装置であって依存先ではなく、各現場の relay は15秒ポーリングで従来どおり完全に動く。
|
|
855
|
+
# ★この呼び出しは必ず cleanup_stale_units より後に置くこと。耳を台帳へ載せるのはこの中で、
|
|
856
|
+
# 掃除の例外(CURRENT_MACHINE_UNITS)は上で先に効かせてある(test/setup-ear-order.test.ts が固定)。
|
|
857
|
+
if [[ "$OS_KIND" == "mac" ]]; then
|
|
858
|
+
EAR_INSTALL="$SCRIPT_DIR/../launchd/ear-install.sh"
|
|
859
|
+
else
|
|
860
|
+
EAR_INSTALL="$SCRIPT_DIR/../systemd/ear-install.sh"
|
|
861
|
+
fi
|
|
862
|
+
if [[ -f "$EAR_INSTALL" ]]; then
|
|
863
|
+
bash "$EAR_INSTALL" || warn "耳の設置に失敗しました(relay は15秒ごとの確認で従来どおり動きます)。"
|
|
864
|
+
else
|
|
865
|
+
warn "耳の導入処理が見つかりません: $EAR_INSTALL(耳の設置はスキップ)
|
|
866
|
+
relay は15秒ごとの確認で従来どおり動きます。"
|
|
867
|
+
fi
|
|
868
|
+
|
|
869
|
+
# --- 貼り付け一時ファイルの毎日自動掃除タイマー(機体1回・現場ごとではない) ---
|
|
870
|
+
# ~/.claude/paste-cache に貼り付け由来の平文が溜まるのを、放置でも溜まらない形にする。
|
|
871
|
+
# best-effort: 失敗しても本体セットアップは完了扱いにする。
|
|
872
|
+
PRUNE_INSTALL="$SCRIPT_DIR/systemd/paste-cache-prune-install.sh"
|
|
873
|
+
if [[ "$OS_KIND" == "mac" ]]; then
|
|
874
|
+
# ★Mac 版の毎日掃除タイマーはまだ用意していない(残る穴として ADR-019 に明記)。
|
|
875
|
+
warn "貼り付け一時ファイルの毎日自動掃除は、Mac ではまだ用意していません(手動で消せます:
|
|
876
|
+
find ~/.claude/paste-cache -type f -mtime +1 -delete)"
|
|
877
|
+
elif [[ -f "$PRUNE_INSTALL" ]]; then
|
|
878
|
+
bash "$PRUNE_INSTALL" || warn "貼り付け一時ファイルの自動掃除タイマー設置に失敗しました(セットアップは続行)。"
|
|
318
879
|
else
|
|
319
|
-
warn "
|
|
320
|
-
echo "[setup] 手動で systemd を設定する場合は sales-template/systemd/README.md を参照してください。"
|
|
880
|
+
warn "paste-cache-prune-install.sh が見つかりません: $PRUNE_INSTALL(自動掃除タイマーはスキップ)"
|
|
321
881
|
fi
|
|
322
882
|
|
|
323
883
|
# ─── ⑦ 職人起動の案内 ────────────────────────────────────────────────────────
|
|
324
|
-
|
|
884
|
+
# ─── ⑦ 自己点検(★お客さまが読んで分かる形で出す) ──────────────────────────
|
|
885
|
+
# 【なぜ要るか】以前の確認手段は systemctl / journalctl / 番人の手動テストの3つだけで、
|
|
886
|
+
# どれも黒い画面の読み方が要る=60代のお客さまには判断できなかった(調査 3f2337e6)。
|
|
887
|
+
# そこで、インストーラ自身が「置いたものが在るか」を1つずつ見て、○×で出す。
|
|
888
|
+
# ★ここは【確認するだけ】。何も作らない・変えない(何度実行しても同じ)。
|
|
889
|
+
step "⑦ 自己点検(ここまで出れば大丈夫です)"
|
|
890
|
+
|
|
891
|
+
ok_count=0
|
|
892
|
+
ng_lines=()
|
|
893
|
+
mark() { # mark <条件の結果> <項目名> <直し方>
|
|
894
|
+
if [[ "$1" == "ok" ]]; then
|
|
895
|
+
echo " ✓ $2"
|
|
896
|
+
ok_count=$((ok_count + 1))
|
|
897
|
+
else
|
|
898
|
+
echo " ✗ $2"
|
|
899
|
+
ng_lines+=("$2 … $3")
|
|
900
|
+
fi
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
RETRY="setup.sh をもう一度実行してください"
|
|
904
|
+
if [[ -x "$GUARD_HOME/cc2_guard.py" ]]; then
|
|
905
|
+
mark ok "安全装置(番人)が置かれている" ""
|
|
906
|
+
else
|
|
907
|
+
mark ng "安全装置(番人)が置かれている" "$RETRY"
|
|
908
|
+
fi
|
|
909
|
+
if [[ -s "$HOME_SETTINGS" ]]; then
|
|
910
|
+
mark ok "番人の登録ができている" ""
|
|
911
|
+
else
|
|
912
|
+
mark ng "番人の登録ができている" "$RETRY"
|
|
913
|
+
fi
|
|
914
|
+
if [[ -f "$ENV_FILE" ]]; then
|
|
915
|
+
mark ok "管制への接続情報がある" ""
|
|
916
|
+
else
|
|
917
|
+
mark ng "管制への接続情報がある" "$RETRY"
|
|
918
|
+
fi
|
|
919
|
+
if service_is_running "conductor@${SITE}" "com.tyhld.conductor.${SITE}"; then
|
|
920
|
+
mark ok "この現場(${SITE})の常駐が動いている" ""
|
|
921
|
+
else
|
|
922
|
+
mark ng "この現場(${SITE})の常駐が動いている" "$SVC_RESTART_HINT"
|
|
923
|
+
fi
|
|
924
|
+
if service_is_running conductor-ear com.tyhld.conductor.ear; then
|
|
925
|
+
mark ok "指示の受け取り役が動いている" ""
|
|
926
|
+
else
|
|
927
|
+
# 耳は加速装置。無くても15秒ごとの確認で届くので、✗にはするが不安を煽らない。
|
|
928
|
+
mark ng "指示の受け取り役が動いている" \
|
|
929
|
+
"無くても指示は届きます(少し遅くなるだけ)。気になるときはご連絡ください"
|
|
930
|
+
fi
|
|
931
|
+
if command -v claude >/dev/null 2>&1; then
|
|
932
|
+
mark ok "職人(Claude Code)が使える" ""
|
|
933
|
+
else
|
|
934
|
+
mark ng "職人(Claude Code)が使える" "npm i -g @anthropic-ai/claude-code を実行してください"
|
|
935
|
+
fi
|
|
936
|
+
|
|
937
|
+
echo
|
|
938
|
+
if [[ ${#ng_lines[@]} -eq 0 ]]; then
|
|
939
|
+
echo " ★ぜんぶ ✓ です(${ok_count}項目)。この画面のとおりなら設置は成功しています。"
|
|
940
|
+
else
|
|
941
|
+
echo " ★ ✗ が ${#ng_lines[@]} 件あります。次のようにしてください:"
|
|
942
|
+
for l in "${ng_lines[@]}"; do
|
|
943
|
+
echo " - $l"
|
|
944
|
+
done
|
|
945
|
+
echo " 直らないときは、この画面をそのままサポートへお送りください。"
|
|
946
|
+
fi
|
|
947
|
+
|
|
948
|
+
step "⑧ 完了! 職人(Claude Code)の起動方法"
|
|
325
949
|
|
|
326
950
|
cat <<GUIDE
|
|
327
951
|
|
|
328
952
|
采配くんのセットアップが完了しました。
|
|
329
953
|
|
|
330
|
-
|
|
954
|
+
次にやること: Claude Code(職人)を起動してください。下の3行をそのまま貼ります。
|
|
331
955
|
|
|
332
|
-
# 1. tmux
|
|
956
|
+
# 1. 作業部屋(tmux)を用意する
|
|
333
957
|
tmux new-session -d -s "$SITE" -c "$PROJECT_DIR"
|
|
334
958
|
|
|
335
|
-
# 2.
|
|
959
|
+
# 2. 職人を起動する
|
|
336
960
|
tmux send-keys -t "$SITE" "unset ANTHROPIC_API_KEY && claude" C-m
|
|
337
961
|
|
|
338
|
-
# 3.
|
|
962
|
+
# 3. 職人の画面を開く
|
|
339
963
|
tmux attach -t "$SITE"
|
|
340
964
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
965
|
+
──────────────────────────────────────────────────────────
|
|
966
|
+
★いちばん確実な「動いています」の確かめ方
|
|
967
|
+
──────────────────────────────────────────────────────────
|
|
968
|
+
|
|
969
|
+
管制の画面(采配くん)をブラウザで開いてください。
|
|
970
|
+
そこに「${SITE}」が出ていれば、つながって動いています。★これが正の確認方法です。
|
|
971
|
+
|
|
972
|
+
出てこないときは、次の順に試してください。
|
|
973
|
+
1. 職人の画面(上の3行)を開いたままにする
|
|
974
|
+
2. 1〜2分待ってから、管制の画面を開き直す
|
|
975
|
+
3. それでも出ないときは: ${SVC_RESTART_CMD}
|
|
976
|
+
4. それでも出ないときは、この画面をそのままサポートへお送りください
|
|
977
|
+
|
|
978
|
+
──────────────────────────────────────────────────────────
|
|
979
|
+
困ったとき(黒い画面のコマンド・補助)
|
|
980
|
+
──────────────────────────────────────────────────────────
|
|
981
|
+
状態を見る: ${SVC_STATUS_CMD}
|
|
982
|
+
動きの記録: ${SVC_LOG_CMD}
|
|
983
|
+
入れ直す: ${SVC_RESTART_CMD}
|
|
984
|
+
止める: ${SVC_STOP_CMD}
|
|
985
|
+
合言葉を入れ直す: rm ~/.conductor.env → そのあと setup.sh をもう一度実行
|
|
349
986
|
|
|
350
987
|
GUIDE
|