@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
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# install.sh — 采配くんを「1行」で入れる入口。
|
|
4
|
+
#
|
|
5
|
+
# お客さまが打つのはこの1行だけ:
|
|
6
|
+
# curl -fsSL https://ai-kana.ty-hld.com/setup | bash -s <現場名>
|
|
7
|
+
#
|
|
8
|
+
# 【この台本の役目】
|
|
9
|
+
# setup.sh(既存の設置係)は「道具がそろっている機体」に采配くんを置くための台本で、
|
|
10
|
+
# 道具が無ければ前提チェックで止まる。お客さまはそこで何をすればいいか分からない。
|
|
11
|
+
# =この install.sh は【setup.sh の手前】を受け持つ。道具をそろえ、鍵を用意し、
|
|
12
|
+
# 最後に setup.sh(`conductor setup`)を呼ぶ。★setup.sh 本体は1行も変えない。
|
|
13
|
+
#
|
|
14
|
+
# 【3つの決め(ここを外すと1行では動かない)】
|
|
15
|
+
# ① 冪等 — 済んでいる段は「✓ 済み」と出して飛ばす。何度打っても壊れない。
|
|
16
|
+
# ② 対話は /dev/tty から読む — `curl … | bash` では標準入力が【台本そのもの】に
|
|
17
|
+
# 取られているので `read` は入力を求めずに空を返す(実測 2026-09-03)。
|
|
18
|
+
# 端末そのもの(/dev/tty)から読めば、パイプの中からでも入力を求められる。
|
|
19
|
+
# ③ 止まる段は日本語で案内して止まる — ブラウザのやり取りが要るもの
|
|
20
|
+
# (Claude Code のログイン・GitHub のログイン)と、再起動が要るもの
|
|
21
|
+
# (WSL の systemd 有効化)は人の手番。「何をして、終わったらこの1行をもう一度」
|
|
22
|
+
# と書いて exit 1 する。次に打つと済み判定で飛ぶので、続きから進む。
|
|
23
|
+
#
|
|
24
|
+
# 【sudo について】
|
|
25
|
+
# sudo はパスワードを標準入力ではなく【端末】から読む(man sudo: "read it from the
|
|
26
|
+
# user's terminal"/`-S` は逆に標準入力から読ませる旗)。だからパイプの中でも聞ける。
|
|
27
|
+
# ★ただし「使う予定が無いのに聞く」ことはしない。先に足りない物を数え、
|
|
28
|
+
# 本当に sudo が要るときだけ最初に1回 `sudo -v` でまとめて通してもらう。
|
|
29
|
+
#
|
|
30
|
+
# 【確認だけしたいとき(サポート・検証用)】
|
|
31
|
+
# CONDUCTOR_INSTALL_DRY_RUN=1 bash install.sh <現場名>
|
|
32
|
+
# … 各段の「済んだか」だけを出して、何も入れない・何も変えない・何も聞かない。
|
|
33
|
+
# update-guard.sh / pw-browser-install.sh と同じ「まず確認」の作法に合わせてある。
|
|
34
|
+
#
|
|
35
|
+
# 【入力元を差し替えたいとき(検査用)】
|
|
36
|
+
# CONDUCTOR_INSTALL_TTY=<ファイル> bash install.sh <現場名>
|
|
37
|
+
# … 対話の入力を、本物の端末ではなくそのファイルから読む。既定は /dev/tty。
|
|
38
|
+
# ★検査が本物の端末を掴んで入力待ちで止まらないようにするための口。
|
|
39
|
+
# お客さまの実行では指定しない=今までどおり端末から聞く。
|
|
40
|
+
#
|
|
41
|
+
# 【秘密の扱い】
|
|
42
|
+
# 合言葉は画面に出さない(伏せ字入力)。コマンド引数にも環境変数にも載せない
|
|
43
|
+
# (引数はプロセス一覧から、環境変数は /proc から他人に見えることがある)。
|
|
44
|
+
# ファイルへは umask 077 のもとで書き、書いたあと 600 を明示する。
|
|
45
|
+
#
|
|
46
|
+
set -euo pipefail
|
|
47
|
+
|
|
48
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
49
|
+
# 設定(環境変数で差し替えられる。ふつうは触らない)
|
|
50
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
51
|
+
|
|
52
|
+
# 管制(ai-kana)の既定アドレス。⑪の入力で Enter を押すとこれが使われる。
|
|
53
|
+
DEFAULT_CONSOLE_URL="${CONDUCTOR_DEFAULT_URL:-https://ai-kana.ty-hld.com}"
|
|
54
|
+
|
|
55
|
+
# 入れる Node の版。人と職人(Claude Code)が使う側。
|
|
56
|
+
NODE_MAJOR_WANTED=24
|
|
57
|
+
|
|
58
|
+
# 采配くん本体の最低版(★この値は sales-template/setup.sh と同じにすること)。
|
|
59
|
+
# 【なぜ要るか(実測 2026-09-04)】公開 npm には 0.3.0 しか出ておらず、お客さまは
|
|
60
|
+
# それを掴んでいた。0.3.0 の中身は古い鍵名 CONDUCTOR_SHARED_SECRET を読むので、
|
|
61
|
+
# いまの接続情報(CONDUCTOR_TOKEN)では「未設定です」で落ちる(migakia で再現)。
|
|
62
|
+
# ★食い違うと「入口は通したのに設置係が止める」が起きる。テストで同一を固定してある。
|
|
63
|
+
MIN_CONDUCTOR_VERSION="0.5.0"
|
|
64
|
+
|
|
65
|
+
# nvm の版は固定する(毎回ちがう版が降ってくると再現しない)。
|
|
66
|
+
NVM_VERSION='v0.40.1'
|
|
67
|
+
|
|
68
|
+
# apt で入れる道具。★足りないものだけを入れる。
|
|
69
|
+
APT_PACKAGES=(
|
|
70
|
+
build-essential # ネイティブ拡張のビルド(node-gyp 等)
|
|
71
|
+
git # 版管理。職人の作業の土台
|
|
72
|
+
gh # GitHub CLI。職人が PR を作るのに使う
|
|
73
|
+
tmux # 職人の作業部屋。指示はこの中へ届く
|
|
74
|
+
jq # JSON の読み取り
|
|
75
|
+
unzip # 配布物の展開
|
|
76
|
+
curl # 取得
|
|
77
|
+
ca-certificates # https の検証
|
|
78
|
+
python3 # 番人(フック)が動くのに要る
|
|
79
|
+
python3-pip
|
|
80
|
+
python3-venv
|
|
81
|
+
rsync
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# 確認だけモード(何も入れない・何も聞かない)。
|
|
85
|
+
DRY_RUN=0
|
|
86
|
+
if [[ "${CONDUCTOR_INSTALL_DRY_RUN:-}" == "1" ]]; then
|
|
87
|
+
DRY_RUN=1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
91
|
+
# 画面への出し方(お客さまが読む。専門用語を並べない)
|
|
92
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
93
|
+
|
|
94
|
+
BAR='──────────────────────────────────────────────────────────────'
|
|
95
|
+
|
|
96
|
+
say() { printf '%s\n' "$*"; }
|
|
97
|
+
step() { printf '\n%s\n%s\n%s\n' "$BAR" "$1" "$BAR"; }
|
|
98
|
+
skip() { printf ' ✓ 済み %s\n' "$1"; }
|
|
99
|
+
doing() { printf ' → いま %s\n' "$1"; }
|
|
100
|
+
plan() { printf ' ・予定 %s\n' "$1"; }
|
|
101
|
+
note() { printf ' %s\n' "$1"; }
|
|
102
|
+
|
|
103
|
+
# 最後にまとめて出すための記録(★何を飛ばして何をやったかが分かるように)。
|
|
104
|
+
SUMMARY=()
|
|
105
|
+
record() { SUMMARY+=("$1"); }
|
|
106
|
+
|
|
107
|
+
# 直せないので止まる(お客さまの手番)。★必ず「何をして、次にどうするか」を書く。
|
|
108
|
+
halt() {
|
|
109
|
+
printf '\n%s\n' "$BAR" >&2
|
|
110
|
+
printf 'ここでいったん止まります(あと1つ、お手数をおかけします)\n' >&2
|
|
111
|
+
printf '%s\n\n' "$BAR" >&2
|
|
112
|
+
printf '%s\n' "$1" >&2
|
|
113
|
+
printf '\n%s\n' "$BAR" >&2
|
|
114
|
+
printf 'この作業が終わったら、さきほどの1行をもう一度そのまま打ってください。\n' >&2
|
|
115
|
+
printf '済んだところは飛ばして、続きから進みます。\n' >&2
|
|
116
|
+
printf '%s\n' "$BAR" >&2
|
|
117
|
+
exit 1
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# 想定していない壊れ方(お客さまには直せないので、そのまま送ってもらう)。
|
|
121
|
+
die() {
|
|
122
|
+
printf '\n[install] うまくいきませんでした: %s\n' "$1" >&2
|
|
123
|
+
printf '[install] この画面をそのままサポートへお送りください。\n' >&2
|
|
124
|
+
exit 1
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
128
|
+
# 端末(/dev/tty)からの入力
|
|
129
|
+
#
|
|
130
|
+
# ★`curl … | bash` では標準入力が台本に取られているので、`read` は入力を求めずに
|
|
131
|
+
# 空を返す(実測 2026-09-03)。入力は必ず端末そのものから読む。
|
|
132
|
+
# ★端末が無い場所(自動実行・CI・パイプだけの実行)では、空のまま進めるより
|
|
133
|
+
# 「端末が要る」と日本語で止まるほうが安全。空の値で鍵ファイルを作ってしまうと、
|
|
134
|
+
# あとで「指示が届かない」だけが起きて原因が分からなくなる。
|
|
135
|
+
#
|
|
136
|
+
# 【入力元の差し替え口(CONDUCTOR_INSTALL_TTY)】
|
|
137
|
+
# 既定は /dev/tty=お客さまの実行では今までどおり本物の端末から読む。
|
|
138
|
+
# ★検査のときだけ、ここを【ふつうのファイル】に向ける。
|
|
139
|
+
# なぜ要るか(実機 2026-09-04): 検査が本物の /dev/tty を掴んでいたため、
|
|
140
|
+
# 端末のある機体で `npm test` を流すと「① 管制のアドレス…」で入力待ちになって
|
|
141
|
+
# 止まった(CI は端末が無いので気づけなかった=すり抜けた)。
|
|
142
|
+
# ★入力元を差し替えられるようにするだけ。段・判定・止まり方は1つも変えない。
|
|
143
|
+
#
|
|
144
|
+
# 【聞く文は標準エラーへ出す】
|
|
145
|
+
# 以前は /dev/tty へ直接書いていたが、入力元を差し替えるとそこが読み取り用の
|
|
146
|
+
# ファイルになる(同じファイルへ書くと入力を壊す)。聞く文は標準エラーへ出す。
|
|
147
|
+
# `curl … | bash` でも sudo のパスワード入力と同じくちゃんと画面に見える。
|
|
148
|
+
# >>> tty-io >>> ★この印の間はテストが切り出して単体で流す。動かすときは消さない。
|
|
149
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
150
|
+
|
|
151
|
+
# 入力元。既定は本物の端末。検査だけがここを差し替える。
|
|
152
|
+
INSTALL_TTY="${CONDUCTOR_INSTALL_TTY:-/dev/tty}"
|
|
153
|
+
|
|
154
|
+
# 入力元を開いて読み取り用の 3 番につなぐ。開けなければ日本語で止まる。
|
|
155
|
+
tty_open() {
|
|
156
|
+
if [[ "${TTY_READY:-0}" == "1" ]]; then
|
|
157
|
+
return 0
|
|
158
|
+
fi
|
|
159
|
+
if ! exec 3<"$INSTALL_TTY" 2>/dev/null; then
|
|
160
|
+
printf '\n[install] ここから先は、お客さまに入力していただく必要があります。\n' >&2
|
|
161
|
+
printf '[install] ★いまの実行には「入力できる画面(端末)」がありません。\n' >&2
|
|
162
|
+
printf '[install] 自動実行や、画面のない環境からは進められません。\n' >&2
|
|
163
|
+
printf '[install] Ubuntu(WSL)の画面をご自分で開いて、そこで1行を打ち直してください。\n' >&2
|
|
164
|
+
exit 1
|
|
165
|
+
fi
|
|
166
|
+
TTY_READY=1
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# 端末へ聞く。 ask_tty <入れる変数名> <聞く文> [既定値]
|
|
170
|
+
ask_tty() {
|
|
171
|
+
local var="$1" prompt="$2" default="${3:-}" answer=''
|
|
172
|
+
tty_open
|
|
173
|
+
if [[ -n "$default" ]]; then
|
|
174
|
+
printf '%s\n' "$prompt" >&2
|
|
175
|
+
printf ' (そのままで良ければ Enter → %s): ' "$default" >&2
|
|
176
|
+
else
|
|
177
|
+
printf '%s\n' "$prompt" >&2
|
|
178
|
+
printf ' : ' >&2
|
|
179
|
+
fi
|
|
180
|
+
IFS= read -r -u 3 answer || answer=''
|
|
181
|
+
if [[ -z "$answer" ]]; then
|
|
182
|
+
answer="$default"
|
|
183
|
+
fi
|
|
184
|
+
printf -v "$var" '%s' "$answer"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# 端末へ聞く(画面に出さない)。 ask_tty_secret <入れる変数名> <聞く文>
|
|
188
|
+
ask_tty_secret() {
|
|
189
|
+
local var="$1" prompt="$2" answer=''
|
|
190
|
+
tty_open
|
|
191
|
+
printf '%s\n' "$prompt" >&2
|
|
192
|
+
printf ' (入力しても画面には出ません): ' >&2
|
|
193
|
+
IFS= read -r -s -u 3 answer || answer=''
|
|
194
|
+
printf '\n' >&2
|
|
195
|
+
printf -v "$var" '%s' "$answer"
|
|
196
|
+
}
|
|
197
|
+
# <<< tty-io <<<
|
|
198
|
+
|
|
199
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
200
|
+
# 済んだかを見る道具(★readlink -f は使わない)
|
|
201
|
+
#
|
|
202
|
+
# readlink -f は【存在しないパスでも】正規化した文字列を返すので、「在るか」の
|
|
203
|
+
# 判定には使えない(実測 2026-09-03: 無い /usr/local/bin/node に対して
|
|
204
|
+
# readlink -f は /usr/local/bin/node を返した)。在るかは -e / -f / -L で見る。
|
|
205
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
206
|
+
|
|
207
|
+
have() { command -v "$1" >/dev/null 2>&1; }
|
|
208
|
+
path_here() { [[ -e "$1" || -L "$1" ]]; }
|
|
209
|
+
|
|
210
|
+
# version_lt <a> <b> — a が b より古いか。
|
|
211
|
+
# ★`sort -V` に頼らない(Mac の sort には無い版がある)。数字3つだけを見る。
|
|
212
|
+
# 読めない字は 0 とみなす(安全側=古い扱いで、更新へ倒す)。
|
|
213
|
+
version_lt() {
|
|
214
|
+
local a="${1%%-*}" b="${2%%-*}" i x y
|
|
215
|
+
local -a A B
|
|
216
|
+
IFS='.' read -r -a A <<< "$a"
|
|
217
|
+
IFS='.' read -r -a B <<< "$b"
|
|
218
|
+
for i in 0 1 2; do
|
|
219
|
+
x="${A[i]:-0}"; y="${B[i]:-0}"
|
|
220
|
+
[[ "$x" =~ ^[0-9]+$ ]] || x=0
|
|
221
|
+
[[ "$y" =~ ^[0-9]+$ ]] || y=0
|
|
222
|
+
if (( x < y )); then return 0; fi
|
|
223
|
+
if (( x > y )); then return 1; fi
|
|
224
|
+
done
|
|
225
|
+
return 1
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
# いま入っている采配くん本体の版(取れなければ空)。
|
|
229
|
+
conductor_version() {
|
|
230
|
+
local v
|
|
231
|
+
v="$(conductor --version 2>/dev/null | tr -d '[:space:]')"
|
|
232
|
+
if [[ "$v" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
|
233
|
+
printf '%s' "${BASH_REMATCH[0]}"
|
|
234
|
+
fi
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
# apt の道具が「もう使える」か。
|
|
238
|
+
#
|
|
239
|
+
# ★名前が入っているかだけでは足りない。python3-venv は【まとめ役の名前】で、実体は
|
|
240
|
+
# python3.12-venv のように版付きの名前で入る。名前だけ見ると、実際には使えている
|
|
241
|
+
# 機体で「足りない」と判定し、要らない導入(と要らないパスワード入力)が走る
|
|
242
|
+
# (実機 2026-09-04: python3.12-venv が入っているのに python3-venv は未登録だった)。
|
|
243
|
+
# =名前で見つからないものは【できるかどうか】で見る。
|
|
244
|
+
apt_has() {
|
|
245
|
+
if dpkg -s "$1" >/dev/null 2>&1; then
|
|
246
|
+
return 0
|
|
247
|
+
fi
|
|
248
|
+
case "$1" in
|
|
249
|
+
python3-venv)
|
|
250
|
+
# venv が実際に使えるかを見る。使えない機体では ensurepip が入っていない。
|
|
251
|
+
python3 -c 'import ensurepip' >/dev/null 2>&1
|
|
252
|
+
;;
|
|
253
|
+
*)
|
|
254
|
+
return 1
|
|
255
|
+
;;
|
|
256
|
+
esac
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
# node のメジャー版(取れなければ 0)。
|
|
260
|
+
node_major() {
|
|
261
|
+
local v
|
|
262
|
+
v="$(node --version 2>/dev/null || true)"
|
|
263
|
+
v="${v#v}"
|
|
264
|
+
v="${v%%.*}"
|
|
265
|
+
if [[ "$v" =~ ^[0-9]+$ ]]; then
|
|
266
|
+
printf '%s' "$v"
|
|
267
|
+
else
|
|
268
|
+
printf '0'
|
|
269
|
+
fi
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
# Claude Code にログイン済みか。★`claude auth status --json` の loggedIn を見る。
|
|
273
|
+
claude_logged_in() {
|
|
274
|
+
local out
|
|
275
|
+
out="$(claude auth status --json 2>/dev/null || true)"
|
|
276
|
+
[[ "$out" == *'"loggedIn"'* ]] || return 1
|
|
277
|
+
printf '%s' "$out" | tr -d ' ' | grep -q '"loggedIn":true'
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
# 実行するか、確認だけか。 run <説明> <コマンド…>
|
|
281
|
+
run() {
|
|
282
|
+
local what="$1"
|
|
283
|
+
shift
|
|
284
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
285
|
+
plan "$what"
|
|
286
|
+
return 0
|
|
287
|
+
fi
|
|
288
|
+
doing "$what"
|
|
289
|
+
"$@"
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
293
|
+
# ⓪ 引数と土台の確認
|
|
294
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
295
|
+
|
|
296
|
+
step '⓪ はじめの確認'
|
|
297
|
+
|
|
298
|
+
SITE="${1:-}"
|
|
299
|
+
|
|
300
|
+
if [[ -z "$SITE" ]]; then
|
|
301
|
+
cat >&2 <<'USAGE'
|
|
302
|
+
|
|
303
|
+
采配くんの設置
|
|
304
|
+
|
|
305
|
+
お仕事の名前(現場名)が要ります。次のように、うしろに名前を付けて打ってください。
|
|
306
|
+
|
|
307
|
+
curl -fsSL https://ai-kana.ty-hld.com/setup | bash -s my-app
|
|
308
|
+
~~~~~~
|
|
309
|
+
ここがお仕事の名前
|
|
310
|
+
|
|
311
|
+
名前の決まり:
|
|
312
|
+
・半角の小文字・数字・ハイフン(-)だけ。例: my-app / shop2 / kenzokun
|
|
313
|
+
・作業用のフォルダ名としてそのまま使います(~/projects/<名前>)
|
|
314
|
+
・分からないときは、弊社からお渡しした案内に書いてある名前をお使いください
|
|
315
|
+
USAGE
|
|
316
|
+
exit 1
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
if [[ ! "$SITE" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
|
|
320
|
+
cat >&2 <<USAGE
|
|
321
|
+
|
|
322
|
+
お仕事の名前に使えない文字が入っています: 「${SITE}」
|
|
323
|
+
|
|
324
|
+
・使えるのは 半角の小文字・数字・ハイフン(-)だけです
|
|
325
|
+
・先頭はアルファベット小文字か数字にしてください
|
|
326
|
+
・例: my-app / shop2 / kenzokun
|
|
327
|
+
|
|
328
|
+
もう一度、名前を直して打ってください。
|
|
329
|
+
USAGE
|
|
330
|
+
exit 1
|
|
331
|
+
fi
|
|
332
|
+
|
|
333
|
+
say " お仕事の名前: ${SITE}"
|
|
334
|
+
PROJECT_DIR="$HOME/projects/$SITE"
|
|
335
|
+
say " 作業フォルダ: ${PROJECT_DIR}"
|
|
336
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
337
|
+
say ' ★確認だけのモードです(何も入れません・何も聞きません)'
|
|
338
|
+
fi
|
|
339
|
+
|
|
340
|
+
# --- OS の見分け -------------------------------------------------------------
|
|
341
|
+
# この入口が面倒を見るのは Windows(WSL2 + Ubuntu)です。Mac は道具の入れ方が
|
|
342
|
+
# まるごと違う(apt が無い・常駐が launchd)ので、この1行では扱わない。
|
|
343
|
+
# ★Mac も setup.sh 自体は対応済み(ADR-019)なので、そちらへ案内する。
|
|
344
|
+
case "$(uname -s)" in
|
|
345
|
+
Linux) : ;;
|
|
346
|
+
Darwin)
|
|
347
|
+
halt "$(cat <<'MSG'
|
|
348
|
+
Mac をお使いのようです。この1行の入口は Windows(WSL2 + Ubuntu)用です。
|
|
349
|
+
|
|
350
|
+
Mac では、次の2行で設置してください。
|
|
351
|
+
|
|
352
|
+
npm i -g @tyhld/conductor
|
|
353
|
+
conductor setup <お仕事の名前>
|
|
354
|
+
|
|
355
|
+
(先に Homebrew で tmux を、npm で Claude Code を入れてログインしておいてください)
|
|
356
|
+
MSG
|
|
357
|
+
)"
|
|
358
|
+
;;
|
|
359
|
+
*)
|
|
360
|
+
halt "この入口は Windows(WSL2 + Ubuntu)用です。ご利用の環境には対応していません。"
|
|
361
|
+
;;
|
|
362
|
+
esac
|
|
363
|
+
|
|
364
|
+
# --- WSL のときは systemd(常駐の仕組み)が要る -------------------------------
|
|
365
|
+
# 【なぜ止めるか】systemd が無いと、采配くんの常駐(指示を受け取り続ける係)を
|
|
366
|
+
# 登録できない。ここを飛ばして先へ進めると、設置は成功したように見えて
|
|
367
|
+
# 指示が1つも届かない。★お客さまが後日いちばん困る形なので、必ずここで止める。
|
|
368
|
+
IS_WSL=0
|
|
369
|
+
if [[ -r /proc/version ]] && grep -qi microsoft /proc/version; then
|
|
370
|
+
IS_WSL=1
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
WSL_GUIDE=$(cat <<'MSG'
|
|
374
|
+
采配くんが動き続けるための仕組み(systemd)が、まだ有効になっていません。
|
|
375
|
+
次の2つを、この順に行ってください。
|
|
376
|
+
|
|
377
|
+
【1】いまのこの画面(Ubuntu)で、次の1行をそのまま打ちます
|
|
378
|
+
(管理者のパスワードを聞かれたら、Windows にログインするときのものではなく
|
|
379
|
+
Ubuntu を最初に開いたときに決めたパスワードです)
|
|
380
|
+
|
|
381
|
+
sudo sh -c 'printf "[boot]\nsystemd=true\n" >> /etc/wsl.conf'
|
|
382
|
+
|
|
383
|
+
【2】Windows のスタートメニューから「PowerShell」または「コマンド プロンプト」を
|
|
384
|
+
開いて、次の1行を打ちます(Ubuntu がいったん閉じます)
|
|
385
|
+
|
|
386
|
+
wsl --shutdown
|
|
387
|
+
|
|
388
|
+
そのあと、もう一度 Ubuntu を開いてください。
|
|
389
|
+
MSG
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
if [[ "$IS_WSL" == "1" ]]; then
|
|
393
|
+
if [[ -r /etc/wsl.conf ]] && grep -Eq '^[[:space:]]*systemd[[:space:]]*=[[:space:]]*true' /etc/wsl.conf; then
|
|
394
|
+
skip 'Windows 側の設定(systemd)'
|
|
395
|
+
else
|
|
396
|
+
halt "$WSL_GUIDE"
|
|
397
|
+
fi
|
|
398
|
+
fi
|
|
399
|
+
|
|
400
|
+
if ! have systemctl; then
|
|
401
|
+
halt "$WSL_GUIDE"
|
|
402
|
+
fi
|
|
403
|
+
if systemctl --user show-environment >/dev/null 2>&1; then
|
|
404
|
+
skip '常駐の仕組みが動いている'
|
|
405
|
+
else
|
|
406
|
+
halt "$WSL_GUIDE"
|
|
407
|
+
fi
|
|
408
|
+
|
|
409
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
410
|
+
# ① 管理者の許可(sudo)を、必要なときだけ最初に1回
|
|
411
|
+
#
|
|
412
|
+
# ★使う予定が無いのに聞かない。先に「足りないもの」を数えて、
|
|
413
|
+
# 本当に管理者の許可が要るときだけまとめて通してもらう。
|
|
414
|
+
# (そうしないと、全部済んでいる機体でも毎回パスワードを聞かれる)
|
|
415
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
416
|
+
|
|
417
|
+
step '① 管理者の許可'
|
|
418
|
+
|
|
419
|
+
APT_MISSING=()
|
|
420
|
+
for p in "${APT_PACKAGES[@]}"; do
|
|
421
|
+
if ! apt_has "$p"; then
|
|
422
|
+
APT_MISSING+=("$p")
|
|
423
|
+
fi
|
|
424
|
+
done
|
|
425
|
+
|
|
426
|
+
NEED_SYSTEM_NODE=0
|
|
427
|
+
path_here /usr/bin/node || NEED_SYSTEM_NODE=1
|
|
428
|
+
|
|
429
|
+
# setup.sh ③ が /usr/local/bin/node のリンクを作るときにも管理者の許可が要る。
|
|
430
|
+
NEED_NODE_LINK=0
|
|
431
|
+
path_here /usr/local/bin/node || NEED_NODE_LINK=1
|
|
432
|
+
|
|
433
|
+
NEED_SUDO=0
|
|
434
|
+
if [[ ${#APT_MISSING[@]} -gt 0 || "$NEED_SYSTEM_NODE" == "1" || "$NEED_NODE_LINK" == "1" ]]; then
|
|
435
|
+
NEED_SUDO=1
|
|
436
|
+
fi
|
|
437
|
+
|
|
438
|
+
if [[ "$NEED_SUDO" == "0" ]]; then
|
|
439
|
+
skip '管理者の許可は要りません(足りないものがありません)'
|
|
440
|
+
record '管理者の許可: 不要'
|
|
441
|
+
elif [[ "$DRY_RUN" == "1" ]]; then
|
|
442
|
+
plan '管理者の許可をまとめて1回もらいます(sudo -v)'
|
|
443
|
+
else
|
|
444
|
+
if ! have sudo; then
|
|
445
|
+
halt 'このパソコンには sudo(管理者の許可を使う仕組み)がありません。サポートへご連絡ください。'
|
|
446
|
+
fi
|
|
447
|
+
if sudo -n -v >/dev/null 2>&1; then
|
|
448
|
+
skip '管理者の許可は既に通っています'
|
|
449
|
+
record '管理者の許可: 既に通っていた'
|
|
450
|
+
else
|
|
451
|
+
say ' これから、パソコンに道具を入れるために【管理者の許可】を何度か使います。'
|
|
452
|
+
say ' いちばん最初に1回だけパスワードをお聞きします(以後は聞きません)。'
|
|
453
|
+
say ' ★Ubuntu を最初に開いたときに決めたパスワードです。'
|
|
454
|
+
tty_open
|
|
455
|
+
if ! sudo -v; then
|
|
456
|
+
halt 'パスワードが通りませんでした。もう一度、同じ1行を打ってやり直してください。'
|
|
457
|
+
fi
|
|
458
|
+
record '管理者の許可: 通した'
|
|
459
|
+
fi
|
|
460
|
+
fi
|
|
461
|
+
|
|
462
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
463
|
+
# ② 道具をそろえる(足りないものだけ)
|
|
464
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
465
|
+
|
|
466
|
+
step '② 道具をそろえる'
|
|
467
|
+
|
|
468
|
+
if [[ ${#APT_MISSING[@]} -eq 0 ]]; then
|
|
469
|
+
skip "道具はそろっています(${#APT_PACKAGES[@]} 個)"
|
|
470
|
+
record '道具: 済み'
|
|
471
|
+
else
|
|
472
|
+
say " 足りないもの: ${APT_MISSING[*]}"
|
|
473
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
474
|
+
plan "足りない ${#APT_MISSING[@]} 個を入れます"
|
|
475
|
+
else
|
|
476
|
+
run '一覧を最新にします' sudo apt-get update -qq \
|
|
477
|
+
|| die 'ソフトの一覧を取れませんでした(インターネット接続をご確認ください)'
|
|
478
|
+
run "${#APT_MISSING[@]} 個の道具を入れます" \
|
|
479
|
+
sudo apt-get install -y "${APT_MISSING[@]}" \
|
|
480
|
+
|| die '道具を入れられませんでした'
|
|
481
|
+
record "道具: ${#APT_MISSING[@]} 個を入れた"
|
|
482
|
+
fi
|
|
483
|
+
fi
|
|
484
|
+
|
|
485
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
486
|
+
# ③ Node(人と職人が使う側)
|
|
487
|
+
#
|
|
488
|
+
# ★nvm 経由で入れる。理由: apt の Node は版が古く、あとから上げにくい。
|
|
489
|
+
# nvm なら「このユーザーの中だけ」で版を切り替えられ、OS 側を壊さない。
|
|
490
|
+
# ★入れたあと、同じ実行の中で使えるように nvm.sh を読み込んで続ける
|
|
491
|
+
# (新しい画面を開き直さなくて済むように)。
|
|
492
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
493
|
+
|
|
494
|
+
step '③ Node を用意する(人と職人が使う分)'
|
|
495
|
+
|
|
496
|
+
NVM_DIR_PATH="${NVM_DIR:-$HOME/.nvm}"
|
|
497
|
+
|
|
498
|
+
if have node && [[ "$(node_major)" -ge "$NODE_MAJOR_WANTED" ]]; then
|
|
499
|
+
skip "Node は用意できています($(node --version))"
|
|
500
|
+
record "Node: 済み($(node --version))"
|
|
501
|
+
elif [[ "$DRY_RUN" == "1" ]]; then
|
|
502
|
+
if [[ -s "$NVM_DIR_PATH/nvm.sh" ]]; then
|
|
503
|
+
plan "Node ${NODE_MAJOR_WANTED} を入れます(切り替えの仕組みは既にあります)"
|
|
504
|
+
else
|
|
505
|
+
plan "切り替えの仕組み(nvm ${NVM_VERSION})と Node ${NODE_MAJOR_WANTED} を入れます"
|
|
506
|
+
fi
|
|
507
|
+
else
|
|
508
|
+
if [[ ! -s "$NVM_DIR_PATH/nvm.sh" ]]; then
|
|
509
|
+
doing "Node を入れ替える仕組み(nvm ${NVM_VERSION})を入れます"
|
|
510
|
+
curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh" | bash \
|
|
511
|
+
|| die 'nvm を入れられませんでした(インターネット接続をご確認ください)'
|
|
512
|
+
fi
|
|
513
|
+
# nvm.sh は自分で未設定の変数を触るので、読み込む間だけ -u を外す。
|
|
514
|
+
set +u
|
|
515
|
+
export NVM_DIR="$NVM_DIR_PATH"
|
|
516
|
+
# shellcheck disable=SC1091 # お客さまの機体で生成されるファイルなので静的には追えない
|
|
517
|
+
. "$NVM_DIR/nvm.sh"
|
|
518
|
+
set -u
|
|
519
|
+
|
|
520
|
+
doing "Node ${NODE_MAJOR_WANTED} を入れます"
|
|
521
|
+
nvm install "$NODE_MAJOR_WANTED" || die "Node ${NODE_MAJOR_WANTED} を入れられませんでした"
|
|
522
|
+
nvm alias default "$NODE_MAJOR_WANTED" >/dev/null \
|
|
523
|
+
|| die 'Node の既定の版を決められませんでした'
|
|
524
|
+
nvm use default >/dev/null || die 'Node に切り替えられませんでした'
|
|
525
|
+
say " 用意できました: $(node --version)"
|
|
526
|
+
record "Node: 入れた($(node --version))"
|
|
527
|
+
fi
|
|
528
|
+
|
|
529
|
+
if [[ "$DRY_RUN" != "1" ]] && ! have npm; then
|
|
530
|
+
die 'npm が見つかりません(Node の導入が途中で終わっている可能性があります)'
|
|
531
|
+
fi
|
|
532
|
+
|
|
533
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
534
|
+
# ④ Node(常駐が使う側)
|
|
535
|
+
#
|
|
536
|
+
# 【なぜ2つ要るか】常駐(systemd)はログインしたときの PATH を引き継がないので、
|
|
537
|
+
# nvm の Node は見えない。決まった場所(/usr/bin/node)にも1本あることが要る。
|
|
538
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
539
|
+
|
|
540
|
+
step '④ Node を用意する(常駐が使う分)'
|
|
541
|
+
|
|
542
|
+
if [[ "$NEED_SYSTEM_NODE" == "0" ]]; then
|
|
543
|
+
skip '常駐用の Node は用意できています'
|
|
544
|
+
record '常駐用 Node: 済み'
|
|
545
|
+
elif [[ "$DRY_RUN" == "1" ]]; then
|
|
546
|
+
plan '常駐用の Node を入れます(sudo apt install nodejs)'
|
|
547
|
+
else
|
|
548
|
+
run '常駐用の Node を入れます' sudo apt-get install -y nodejs \
|
|
549
|
+
|| die '常駐用の Node を入れられませんでした'
|
|
550
|
+
record '常駐用 Node: 入れた'
|
|
551
|
+
fi
|
|
552
|
+
|
|
553
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
554
|
+
# ⑤ pnpm を使えるようにする(corepack)
|
|
555
|
+
#
|
|
556
|
+
# ★pnpm 自体は入れない。corepack を有効にしておけば、リポジトリごとに指定された
|
|
557
|
+
# 正しい版が自動で使われる(版ずれの事故を構造的に防ぐ・ADR-015)。
|
|
558
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
559
|
+
|
|
560
|
+
step '⑤ pnpm を使えるようにする'
|
|
561
|
+
|
|
562
|
+
if have pnpm; then
|
|
563
|
+
skip 'pnpm は使えます'
|
|
564
|
+
record 'pnpm: 済み'
|
|
565
|
+
elif [[ "$DRY_RUN" == "1" ]]; then
|
|
566
|
+
plan 'corepack enable を実行します'
|
|
567
|
+
elif have corepack; then
|
|
568
|
+
run 'pnpm を使えるようにします' corepack enable \
|
|
569
|
+
|| die 'corepack enable に失敗しました'
|
|
570
|
+
record 'pnpm: 使えるようにした'
|
|
571
|
+
else
|
|
572
|
+
# corepack は Node に同梱されているので、ここへ来るのは Node の導入が変な形のとき。
|
|
573
|
+
note '⚠ corepack が見つかりませんでした(pnpm を使う現場では、あとで設定が要ります)'
|
|
574
|
+
record 'pnpm: 見送り(corepack なし)'
|
|
575
|
+
fi
|
|
576
|
+
|
|
577
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
578
|
+
# ⑥ 職人(Claude Code)を入れる
|
|
579
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
580
|
+
|
|
581
|
+
step '⑥ 職人(Claude Code)を入れる'
|
|
582
|
+
|
|
583
|
+
if have claude; then
|
|
584
|
+
skip "職人は入っています($(claude --version 2>/dev/null || echo OK))"
|
|
585
|
+
record '職人: 済み'
|
|
586
|
+
elif [[ "$DRY_RUN" == "1" ]]; then
|
|
587
|
+
plan '職人(Claude Code)を入れます'
|
|
588
|
+
else
|
|
589
|
+
run '職人(Claude Code)を入れます' npm install -g @anthropic-ai/claude-code \
|
|
590
|
+
|| die '職人を入れられませんでした(インターネット接続をご確認ください)'
|
|
591
|
+
have claude || die '職人を入れましたが呼び出せません(PATH の設定をご確認ください)'
|
|
592
|
+
record '職人: 入れた'
|
|
593
|
+
fi
|
|
594
|
+
|
|
595
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
596
|
+
# ⑦ 職人のログイン(★お客さまの手番・ここで止まることがある)
|
|
597
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
598
|
+
|
|
599
|
+
step '⑦ 職人のログイン'
|
|
600
|
+
|
|
601
|
+
CLAUDE_LOGIN_GUIDE=$(cat <<'MSG'
|
|
602
|
+
職人(Claude Code)へのログインが、まだ済んでいません。
|
|
603
|
+
ログインはブラウザで行うため、こちらからは代われません。次のようにしてください。
|
|
604
|
+
|
|
605
|
+
【1】この画面で、次の1行を打ちます
|
|
606
|
+
|
|
607
|
+
claude auth login
|
|
608
|
+
|
|
609
|
+
【2】画面に出てくる案内にしたがって、ブラウザでログインします
|
|
610
|
+
(ブラウザが自動で開かないときは、画面に出た住所をコピーして
|
|
611
|
+
ご自分でブラウザに貼ってください)
|
|
612
|
+
|
|
613
|
+
【3】「ログインしました」と出たら終わりです
|
|
614
|
+
MSG
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
618
|
+
if have claude && claude_logged_in; then
|
|
619
|
+
skip '職人はログイン済みです'
|
|
620
|
+
else
|
|
621
|
+
plan 'ログインが済んでいないため、ここで案内して止まります'
|
|
622
|
+
fi
|
|
623
|
+
elif claude_logged_in; then
|
|
624
|
+
skip '職人はログイン済みです'
|
|
625
|
+
record '職人のログイン: 済み'
|
|
626
|
+
else
|
|
627
|
+
halt "$CLAUDE_LOGIN_GUIDE"
|
|
628
|
+
fi
|
|
629
|
+
|
|
630
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
631
|
+
# ⑧ GitHub のログイン(★お客さまの手番・ここで止まることがある)
|
|
632
|
+
#
|
|
633
|
+
# 【なぜ要るか】職人は作業のまとまりごとに GitHub 上で「変更のお知らせ(PR)」を
|
|
634
|
+
# 作る。ログインしていないとそこで止まってしまう。
|
|
635
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
636
|
+
|
|
637
|
+
step '⑧ GitHub のログイン'
|
|
638
|
+
|
|
639
|
+
GH_LOGIN_GUIDE=$(cat <<'MSG'
|
|
640
|
+
GitHub(プログラムの保管場所)へのログインが、まだ済んでいません。
|
|
641
|
+
こちらもブラウザでのやり取りが要るため、代われません。次のようにしてください。
|
|
642
|
+
|
|
643
|
+
【1】この画面で、次の1行を打ちます
|
|
644
|
+
|
|
645
|
+
gh auth login
|
|
646
|
+
|
|
647
|
+
【2】聞かれたら、次のように選びます(矢印キーで選んで Enter)
|
|
648
|
+
|
|
649
|
+
Where do you use GitHub? → GitHub.com
|
|
650
|
+
What is your preferred protocol? → HTTPS
|
|
651
|
+
Authenticate Git with your ...? → Y(はい)
|
|
652
|
+
How would you like to authenticate? → Login with a web browser
|
|
653
|
+
|
|
654
|
+
【3】画面に8文字の合言葉が出ます。それをブラウザの画面に貼り付けます
|
|
655
|
+
【4】「Logged in as ...」と出たら終わりです
|
|
656
|
+
MSG
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
gh_logged_in() { gh auth status >/dev/null 2>&1; }
|
|
660
|
+
|
|
661
|
+
# git 側の受け渡し(credential helper)が既にそろっているか。
|
|
662
|
+
gh_git_ready() {
|
|
663
|
+
git config --global --get-regexp '^credential\.https://github\.com\.helper$' >/dev/null 2>&1
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
667
|
+
if have gh && gh_logged_in; then
|
|
668
|
+
if gh_git_ready; then
|
|
669
|
+
skip 'GitHub はログイン済み・git の受け渡しもそろっています'
|
|
670
|
+
else
|
|
671
|
+
plan 'git の受け渡しをそろえます(gh auth setup-git)'
|
|
672
|
+
fi
|
|
673
|
+
else
|
|
674
|
+
plan 'ログインが済んでいないため、ここで案内して止まります'
|
|
675
|
+
fi
|
|
676
|
+
elif ! have gh; then
|
|
677
|
+
die 'gh(GitHub の道具)が見つかりません(②で入るはずのものです)'
|
|
678
|
+
elif ! gh_logged_in; then
|
|
679
|
+
halt "$GH_LOGIN_GUIDE"
|
|
680
|
+
else
|
|
681
|
+
skip 'GitHub はログイン済みです'
|
|
682
|
+
if gh_git_ready; then
|
|
683
|
+
skip 'git の受け渡しもそろっています'
|
|
684
|
+
record 'GitHub: 済み'
|
|
685
|
+
else
|
|
686
|
+
run 'git の受け渡しをそろえます' gh auth setup-git \
|
|
687
|
+
|| die 'git の受け渡しをそろえられませんでした'
|
|
688
|
+
record 'GitHub: 受け渡しをそろえた'
|
|
689
|
+
fi
|
|
690
|
+
fi
|
|
691
|
+
|
|
692
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
693
|
+
# ⑨ 采配くん本体を入れる
|
|
694
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
695
|
+
|
|
696
|
+
step '⑨ 采配くん本体を入れる'
|
|
697
|
+
|
|
698
|
+
# ★入っていても【古すぎないか】を見る。古ければ更新する(実測 2026-09-04)。
|
|
699
|
+
# 公開 npm には 0.3.0 しか出ておらず、お客さまはそれを掴んでいた。0.3.0 は古い鍵名
|
|
700
|
+
# (CONDUCTOR_SHARED_SECRET)を読むので、いまの接続情報では「未設定です」で落ちる。
|
|
701
|
+
# =ここで気づかないと「設置は成功したのに指示が1つも届かない」で終わる。
|
|
702
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
703
|
+
if have conductor; then
|
|
704
|
+
CONDUCTOR_VER="$(conductor_version)"
|
|
705
|
+
if [[ -n "$CONDUCTOR_VER" ]] && ! version_lt "$CONDUCTOR_VER" "$MIN_CONDUCTOR_VERSION"; then
|
|
706
|
+
skip "采配くん本体は入っています(${CONDUCTOR_VER} ≧ ${MIN_CONDUCTOR_VERSION})"
|
|
707
|
+
else
|
|
708
|
+
plan "采配くん本体が古いので更新します(いま ${CONDUCTOR_VER:-不明} → ${MIN_CONDUCTOR_VERSION} 以上)"
|
|
709
|
+
fi
|
|
710
|
+
else
|
|
711
|
+
plan '采配くん本体を入れます(npm i -g @tyhld/conductor)'
|
|
712
|
+
fi
|
|
713
|
+
elif have conductor && [[ -n "$(conductor_version)" ]] \
|
|
714
|
+
&& ! version_lt "$(conductor_version)" "$MIN_CONDUCTOR_VERSION"; then
|
|
715
|
+
skip "采配くん本体は入っています($(conductor_version) ≧ ${MIN_CONDUCTOR_VERSION})"
|
|
716
|
+
record "采配くん本体: 済み($(conductor_version))"
|
|
717
|
+
elif have conductor; then
|
|
718
|
+
say " いま入っている版: $(conductor_version || echo '読み取れません')(${MIN_CONDUCTOR_VERSION} 以上が必要です)"
|
|
719
|
+
run '采配くん本体を新しくします' npm install -g @tyhld/conductor@latest \
|
|
720
|
+
|| die '采配くん本体を新しくできませんでした(インターネット接続をご確認ください)'
|
|
721
|
+
CONDUCTOR_VER="$(conductor_version)"
|
|
722
|
+
if [[ -n "$CONDUCTOR_VER" ]] && version_lt "$CONDUCTOR_VER" "$MIN_CONDUCTOR_VERSION"; then
|
|
723
|
+
halt "$(cat <<MSG
|
|
724
|
+
采配くん本体の版が古すぎます(いま ${CONDUCTOR_VER} / ${MIN_CONDUCTOR_VERSION} 以上が必要です)。
|
|
725
|
+
新しくしようとしましたが、取ってこられませんでした。
|
|
726
|
+
|
|
727
|
+
★古い版のままでは、設置できても指示が1つも届きません(鍵の名前が違うためです)。
|
|
728
|
+
|
|
729
|
+
次の1行をお試しください。
|
|
730
|
+
|
|
731
|
+
npm install -g @tyhld/conductor@latest
|
|
732
|
+
|
|
733
|
+
それでも直らないときは、この画面をそのままサポートへお送りください。
|
|
734
|
+
MSG
|
|
735
|
+
)"
|
|
736
|
+
fi
|
|
737
|
+
record "采配くん本体: 新しくした(${CONDUCTOR_VER:-不明})"
|
|
738
|
+
else
|
|
739
|
+
run '采配くん本体を入れます' npm install -g @tyhld/conductor \
|
|
740
|
+
|| die '采配くん本体を入れられませんでした(インターネット接続をご確認ください)'
|
|
741
|
+
have conductor || die '采配くん本体を入れましたが呼び出せません(PATH の設定をご確認ください)'
|
|
742
|
+
record "采配くん本体: 入れた($(conductor_version))"
|
|
743
|
+
fi
|
|
744
|
+
|
|
745
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
746
|
+
# ⑩ 作業フォルダを作る
|
|
747
|
+
#
|
|
748
|
+
# ★setup.sh ① は「このフォルダが既に在ること」を前提にしていて、無ければ止まる。
|
|
749
|
+
# 1行の入口では、ここで先に作っておく。
|
|
750
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
751
|
+
|
|
752
|
+
step '⑩ 作業フォルダを用意する'
|
|
753
|
+
|
|
754
|
+
if [[ -d "$PROJECT_DIR" ]]; then
|
|
755
|
+
skip "作業フォルダはあります(${PROJECT_DIR})"
|
|
756
|
+
record '作業フォルダ: 済み'
|
|
757
|
+
elif [[ "$DRY_RUN" == "1" ]]; then
|
|
758
|
+
plan "作業フォルダを作ります(${PROJECT_DIR})"
|
|
759
|
+
else
|
|
760
|
+
run "作業フォルダを作ります(${PROJECT_DIR})" mkdir -p "$PROJECT_DIR" \
|
|
761
|
+
|| die "作業フォルダを作れませんでした: ${PROJECT_DIR}"
|
|
762
|
+
record '作業フォルダ: 作った'
|
|
763
|
+
fi
|
|
764
|
+
|
|
765
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
766
|
+
# ⑪ 管制への接続情報(★合言葉は画面に出さない)
|
|
767
|
+
#
|
|
768
|
+
# ★ここで先に作っておくと、このあとの setup.sh ④ は「既にある→スキップ」になる。
|
|
769
|
+
# =setup.sh 本体を1行も変えずに、パイプの中でも入力できるようになる。
|
|
770
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
771
|
+
|
|
772
|
+
step '⑪ 管制への接続情報'
|
|
773
|
+
|
|
774
|
+
ENV_FILE="$HOME/.conductor.env"
|
|
775
|
+
|
|
776
|
+
# ★入力の受け皿は先に空で用意する(set -u のもとで安全にし、静的検査にも見えるように)。
|
|
777
|
+
CONSOLE_URL=''
|
|
778
|
+
CONSOLE_TOKEN=''
|
|
779
|
+
|
|
780
|
+
if [[ -f "$ENV_FILE" ]]; then
|
|
781
|
+
skip '接続情報はあります'
|
|
782
|
+
# 権限が緩んでいたら締める(常駐側も起動時に直すが、ここでも揃えておく)。
|
|
783
|
+
if [[ "$DRY_RUN" != "1" ]]; then
|
|
784
|
+
chmod 600 "$ENV_FILE" 2>/dev/null || true
|
|
785
|
+
fi
|
|
786
|
+
record '接続情報: 済み'
|
|
787
|
+
note '★入れ直したいときは rm ~/.conductor.env のあと、もう一度この1行を打ってください'
|
|
788
|
+
elif [[ "$DRY_RUN" == "1" ]]; then
|
|
789
|
+
plan '管制のアドレスと合言葉をお聞きして、接続情報を作ります'
|
|
790
|
+
else
|
|
791
|
+
say ' 管制(采配くんの画面)につなぐための情報を2つお聞きします。'
|
|
792
|
+
say ' ★どちらも弊社からお渡しした案内に書いてあります。'
|
|
793
|
+
say ''
|
|
794
|
+
|
|
795
|
+
ask_tty CONSOLE_URL ' ① 管制のアドレス' "$DEFAULT_CONSOLE_URL"
|
|
796
|
+
[[ -n "$CONSOLE_URL" ]] || halt '管制のアドレスが空でした。もう一度この1行を打ってやり直してください。'
|
|
797
|
+
case "$CONSOLE_URL" in
|
|
798
|
+
https://*) : ;;
|
|
799
|
+
*) halt "管制のアドレスは https:// から始まります(入力: ${CONSOLE_URL})。案内をご確認のうえ、もう一度この1行を打ってください。" ;;
|
|
800
|
+
esac
|
|
801
|
+
|
|
802
|
+
ask_tty_secret CONSOLE_TOKEN ' ② 合言葉'
|
|
803
|
+
if [[ -z "$CONSOLE_TOKEN" ]]; then
|
|
804
|
+
halt '合言葉が空でした。もう一度この1行を打ってやり直してください。'
|
|
805
|
+
fi
|
|
806
|
+
say " 受け取りました(${#CONSOLE_TOKEN} 文字)。画面には出していません。"
|
|
807
|
+
|
|
808
|
+
# ★作る瞬間から他人に読ませない。umask で作り、そのあと 600 を明示する。
|
|
809
|
+
# 合言葉はコマンド引数にも環境変数にも載せない(プロセス一覧・/proc から見えるため)。
|
|
810
|
+
( umask 077; printf 'CONDUCTOR_URL=%s\nCONDUCTOR_TOKEN=%s\n' "$CONSOLE_URL" "$CONSOLE_TOKEN" > "$ENV_FILE" ) \
|
|
811
|
+
|| die "接続情報を書けませんでした: ${ENV_FILE}"
|
|
812
|
+
chmod 600 "$ENV_FILE" || die "接続情報の権限を直せませんでした: ${ENV_FILE}"
|
|
813
|
+
unset CONSOLE_TOKEN
|
|
814
|
+
say ' 接続情報を作りました(本人だけが読める形にしました)'
|
|
815
|
+
record '接続情報: 作った'
|
|
816
|
+
fi
|
|
817
|
+
|
|
818
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
819
|
+
# ⑫ 設置(既存の setup.sh を呼ぶ)
|
|
820
|
+
#
|
|
821
|
+
# ★ここから先は既存の設置係にそのまま任せる。番人の配置・常駐の登録・自己点検(⑦)
|
|
822
|
+
# ・次にやることの案内(⑧)は、すべて setup.sh が持っている。写しを作らない。
|
|
823
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
824
|
+
|
|
825
|
+
step '⑫ 設置する'
|
|
826
|
+
|
|
827
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
828
|
+
plan "conductor setup ${SITE} を実行します(番人の配置・常駐の登録・自己点検)"
|
|
829
|
+
else
|
|
830
|
+
doing '采配くんを設置します(ここから先は設置係が引き継ぎます)'
|
|
831
|
+
say ''
|
|
832
|
+
conductor setup "$SITE" || die '設置に失敗しました(上に出ている内容をサポートへお送りください)'
|
|
833
|
+
record '設置: 実行した'
|
|
834
|
+
fi
|
|
835
|
+
|
|
836
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
837
|
+
# ⑬ 締め(★自己点検の○×は、すぐ上に設置係が出しています)
|
|
838
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
839
|
+
|
|
840
|
+
step '⑬ ここまでのまとめ'
|
|
841
|
+
|
|
842
|
+
# ★空の配列を "${arr[@]}" で展開すると、古い bash では set -u に叱られる。
|
|
843
|
+
# お客さまの機体の bash の版は選べないので、中身があるときだけ回す。
|
|
844
|
+
if [[ ${#SUMMARY[@]} -gt 0 ]]; then
|
|
845
|
+
for line in "${SUMMARY[@]}"; do
|
|
846
|
+
note "・${line}"
|
|
847
|
+
done
|
|
848
|
+
else
|
|
849
|
+
note '(何も変えていません)'
|
|
850
|
+
fi
|
|
851
|
+
|
|
852
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
853
|
+
say ''
|
|
854
|
+
say '★確認だけのモードで終わりました。実際に入れるときは、この行から'
|
|
855
|
+
say ' CONDUCTOR_INSTALL_DRY_RUN=1 を外して打ってください。'
|
|
856
|
+
exit 0
|
|
857
|
+
fi
|
|
858
|
+
|
|
859
|
+
cat <<GUIDE
|
|
860
|
+
|
|
861
|
+
${BAR}
|
|
862
|
+
★すぐ上に出ている「自己点検」の○×をご覧ください
|
|
863
|
+
${BAR}
|
|
864
|
+
|
|
865
|
+
ぜんぶ ✓ になっていれば、設置は成功しています。
|
|
866
|
+
✗ が残っているときは、その行に書いてある直し方をお試しください。
|
|
867
|
+
|
|
868
|
+
${BAR}
|
|
869
|
+
次にやること — 職人(Claude Code)を動かす
|
|
870
|
+
${BAR}
|
|
871
|
+
|
|
872
|
+
下の3行を、そのままコピーして貼ってください。
|
|
873
|
+
|
|
874
|
+
tmux new-session -d -s "${SITE}" -c "${PROJECT_DIR}"
|
|
875
|
+
tmux send-keys -t "${SITE}" "unset ANTHROPIC_API_KEY && claude" C-m
|
|
876
|
+
tmux attach -t "${SITE}"
|
|
877
|
+
|
|
878
|
+
(1行目で作業部屋を用意し、2行目で職人を呼び、3行目でその画面を開きます)
|
|
879
|
+
|
|
880
|
+
${BAR}
|
|
881
|
+
いちばん確実な「動いています」の確かめ方
|
|
882
|
+
${BAR}
|
|
883
|
+
|
|
884
|
+
管制の画面(采配くん)をブラウザで開いてください。
|
|
885
|
+
そこに「${SITE}」が出ていれば、つながって動いています。
|
|
886
|
+
|
|
887
|
+
出てこないときは、職人の画面を開いたまま1〜2分待って、開き直してください。
|
|
888
|
+
それでも出ないときは、この画面をそのままサポートへお送りください。
|
|
889
|
+
|
|
890
|
+
GUIDE
|