@tyhld/conductor 0.3.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.
@@ -0,0 +1,47 @@
1
+ # conductor@.service — 采配くん常駐サービス(systemd ユーザーサービス テンプレート)。
2
+ #
3
+ # PC起動時に自動で采配くんの relay(指示の自動流し込み)+ heartbeat を常駐させる。
4
+ # 落ちても Restart=always で自動復活する。
5
+ #
6
+ # テンプレート方式:
7
+ # conductor@<プロジェクト名>.service で1サービス。%i に <プロジェクト名> が入る。
8
+ # 例) systemctl --user enable --now conductor@my-app
9
+ #
10
+ # 導入: sales-template/systemd/install.sh が配置・enable・linger まで面倒を見る。
11
+ #
12
+ # 鍵: %h/.conductor.env(chmod 600)を EnvironmentFile で読み込む。
13
+ # 中身:
14
+ # CONDUCTOR_URL=https://<采配くん管制サーバURL>
15
+ # CONDUCTOR_SHARED_SECRET=<シークレット>
16
+ #
17
+ # WorkingDirectory: 既定は %h/projects/%i。プロジェクト名とディレクトリ名が異なる場合は
18
+ # install.sh が drop-in で WorkingDirectory を上書きする。
19
+ #
20
+ # ExecStart のパス解決: systemd ユーザーサービスはシェルの PATH を継承しない。
21
+ # install.sh が npm グローバルインストール先から conductor コマンドの実体パスを検出し、
22
+ # __CONDUCTOR_BIN__ を実際のフルパスに置換してからユニットを配置する。
23
+ # node 自体も /usr/local/bin/node にシンボリックリンクを作成して解決する。
24
+ #
25
+ # relay: heartbeat + 指示の自動流し込みを常駐で有効化。
26
+ # 送信前ガードが「プロジェクト名と一致する tmux セッションが存在し、
27
+ # Claude Code がアイドルで待機している時だけ送る/それ以外はスキップ」を担保する。
28
+
29
+ [Unit]
30
+ Description=Conductor heartbeat (%i)
31
+ After=network-online.target
32
+ Wants=network-online.target
33
+
34
+ [Service]
35
+ Type=simple
36
+ EnvironmentFile=%h/.conductor.env
37
+ # relay(指示の自動流し込み)を常駐で有効化。送信前ガードで誤爆を防止。
38
+ Environment=CONDUCTOR_RELAY_ENABLED=true
39
+ Environment=CONDUCTOR_RELAY_INTERVAL_SEC=15
40
+ WorkingDirectory=%h/projects/%i
41
+ # __CONDUCTOR_BIN__ は install.sh が配置時に conductor コマンドのフルパスに置換する。
42
+ ExecStart=__CONDUCTOR_BIN__ start -s %i -t working -a "%i常駐"
43
+ Restart=always
44
+ RestartSec=5
45
+
46
+ [Install]
47
+ WantedBy=default.target
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # install.sh — 采配くん常駐サービスを顧客PCにインストールする。
4
+ #
5
+ # やること:
6
+ # 1. node の実体パスを検出し、systemd から見える場所にシンボリックリンクを作成。
7
+ # 2. conductor コマンドの実体パスを検出。
8
+ # 3. ユニットファイル conductor@.service を配置(__CONDUCTOR_BIN__ をフルパスに置換)。
9
+ # 4. daemon-reload + enable --now。
10
+ # 5. loginctl enable-linger(ログオフ中も常駐)。
11
+ #
12
+ # 使い方:
13
+ # bash install.sh <プロジェクト名> [プロジェクトディレクトリ]
14
+ #
15
+ # 例:
16
+ # bash install.sh my-app # ~/projects/my-app を想定
17
+ # bash install.sh my-app /home/user/work/my-app # ディレクトリを明示指定
18
+ #
19
+ # 前提:
20
+ # - Linux で systemd が有効(WSL の場合: /etc/wsl.conf の [boot] systemd=true)。
21
+ # - ~/.conductor.env が用意済み(CONDUCTOR_URL / CONDUCTOR_SHARED_SECRET)。
22
+ # - @tyhld/conductor が npm グローバルインストール済み。
23
+ # - node がインストール済み(fnm / nvm / volta / システムパッケージ いずれか)。
24
+ # - sudo が使える(node symlink を /usr/local/bin に作成するため)。
25
+ #
26
+ # 冪等: 何度実行しても安全(既存の symlink・ユニット・enable を上書き更新する)。
27
+ #
28
+ set -euo pipefail
29
+
30
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31
+
32
+ # --- 引数チェック -----------------------------------------------------------
33
+ if [[ $# -lt 1 || -z "${1:-}" ]]; then
34
+ echo "使い方: bash install.sh <プロジェクト名> [プロジェクトディレクトリ]" >&2
35
+ echo "例: bash install.sh my-app" >&2
36
+ echo "例: bash install.sh my-app /home/user/work/my-app" >&2
37
+ exit 1
38
+ fi
39
+
40
+ SITE="$1"
41
+ PROJECT_DIR="${2:-$HOME/projects/$SITE}"
42
+
43
+ # --- ヘルパ -------------------------------------------------------------------
44
+ fail() { echo "エラー: $1" >&2; exit 1; }
45
+ warn() { echo "[install] warn: $1" >&2; }
46
+
47
+ # --- 事前チェック -----------------------------------------------------------
48
+ command -v systemctl >/dev/null 2>&1 || \
49
+ fail "systemctl が見つかりません。systemd が有効か確認してください。"
50
+
51
+ systemctl --user show-environment >/dev/null 2>&1 || \
52
+ fail "systemd のユーザーセッションに接続できません(systemctl --user が不可)。"
53
+
54
+ UNIT_SRC="$SCRIPT_DIR/conductor@.service"
55
+ [[ -f "$UNIT_SRC" ]] || fail "ユニットファイルがありません: $UNIT_SRC"
56
+
57
+ [[ -d "$PROJECT_DIR" ]] || fail "プロジェクトディレクトリが存在しません: $PROJECT_DIR"
58
+
59
+ ENV_FILE="$HOME/.conductor.env"
60
+ [[ -f "$ENV_FILE" ]] || \
61
+ fail "鍵ファイル $ENV_FILE がありません。先に setup.sh を実行するか、手動で作成してください。"
62
+
63
+ # --- 1) node PATH 問題の解決 ------------------------------------------------
64
+ # systemd 既定 PATH = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
65
+ # fnm/nvm/volta の node はここにないため /usr/local/bin/node に symlink を作成。
66
+ SYMLINK_PATH="/usr/local/bin/node"
67
+
68
+ NODE_PATH=""
69
+ if command -v node >/dev/null 2>&1; then
70
+ NODE_PATH="$(readlink -f "$(command -v node)")"
71
+ fi
72
+ [[ -z "$NODE_PATH" ]] && fail "node が見つかりません。Node.js 18 以上をインストールしてください。"
73
+
74
+ NODE_VERSION="$(node --version 2>/dev/null || echo 'unknown')"
75
+ echo "[install] 検出した node: $NODE_PATH ($NODE_VERSION)"
76
+
77
+ need_symlink=true
78
+ if [[ -f "$SYMLINK_PATH" || -L "$SYMLINK_PATH" ]]; then
79
+ existing_target="$(readlink -f "$SYMLINK_PATH" 2>/dev/null || echo '')"
80
+ if [[ "$existing_target" == "$NODE_PATH" ]]; then
81
+ echo "[install] $SYMLINK_PATH は既に正しいリンクです → スキップ"
82
+ need_symlink=false
83
+ else
84
+ echo "[install] $SYMLINK_PATH を $NODE_PATH へ更新します"
85
+ fi
86
+ fi
87
+
88
+ if $need_symlink; then
89
+ echo "[install] $SYMLINK_PATH → $NODE_PATH のシンボリックリンクを作成します"
90
+ if [[ -w "$(dirname "$SYMLINK_PATH")" ]]; then
91
+ ln -sf "$NODE_PATH" "$SYMLINK_PATH"
92
+ else
93
+ echo "[install] /usr/local/bin への書き込みに sudo が必要です"
94
+ sudo ln -sf "$NODE_PATH" "$SYMLINK_PATH"
95
+ fi
96
+ echo "[install] symlink 作成完了"
97
+ fi
98
+
99
+ if ! PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" command -v node >/dev/null 2>&1; then
100
+ fail "node symlink を作成しましたが、systemd 既定 PATH で node が見つかりません。"
101
+ fi
102
+ echo "[install] systemd PATH での node 解決: OK"
103
+
104
+ # --- 2) conductor コマンドのパス解決 -----------------------------------------
105
+ # npm グローバルインストール先は環境依存(~/.npm-global/bin, /usr/local/bin,
106
+ # ~/.volta/bin 等)で systemd PATH にない可能性がある。
107
+ # ユニット配置時に ExecStart にフルパスを書き込んで根治する。
108
+ CONDUCTOR_BIN=""
109
+ if command -v conductor >/dev/null 2>&1; then
110
+ CONDUCTOR_BIN="$(command -v conductor)"
111
+ [[ -L "$CONDUCTOR_BIN" ]] && CONDUCTOR_BIN="$(readlink -f "$CONDUCTOR_BIN")"
112
+ fi
113
+
114
+ if [[ -z "$CONDUCTOR_BIN" ]]; then
115
+ fail "conductor コマンドが見つかりません。先に npm i -g @tyhld/conductor を実行してください。"
116
+ fi
117
+
118
+ echo "[install] 検出した conductor: $CONDUCTOR_BIN"
119
+
120
+ # conductor が実際に動くか簡易検証
121
+ if ! "$CONDUCTOR_BIN" --help >/dev/null 2>&1; then
122
+ warn "conductor コマンドの実行に失敗しました。インストールを確認してください。"
123
+ fi
124
+
125
+ # --- 3) ユニットファイルを配置(__CONDUCTOR_BIN__ をフルパスに置換) ----------
126
+ UNIT_DIR="$HOME/.config/systemd/user"
127
+ UNIT_DST="$UNIT_DIR/conductor@.service"
128
+ mkdir -p "$UNIT_DIR"
129
+
130
+ # テンプレートをコピーし、プレースホルダをフルパスに置換
131
+ sed "s|__CONDUCTOR_BIN__|${CONDUCTOR_BIN}|g" "$UNIT_SRC" > "$UNIT_DST"
132
+ echo "[install] 配置: $UNIT_DST (ExecStart=$CONDUCTOR_BIN)"
133
+
134
+ # --- 4) WorkingDirectory の解決 -----------------------------------------------
135
+ DEFAULT_DIR="$HOME/projects/$SITE"
136
+ DROPIN_DIR="$UNIT_DIR/conductor@${SITE}.service.d"
137
+ DROPIN_FILE="$DROPIN_DIR/override.conf"
138
+
139
+ if [[ "$PROJECT_DIR" != "$DEFAULT_DIR" ]]; then
140
+ mkdir -p "$DROPIN_DIR"
141
+ cat > "$DROPIN_FILE" <<DROPIN
142
+ # install.sh が WorkingDirectory を上書き(プロジェクト名とディレクトリが異なるため)。
143
+ [Service]
144
+ WorkingDirectory=$PROJECT_DIR
145
+ DROPIN
146
+ echo "[install] drop-in: WorkingDirectory を $PROJECT_DIR に上書き"
147
+ elif [[ -f "$DROPIN_FILE" ]]; then
148
+ rm -f "$DROPIN_FILE"
149
+ rmdir "$DROPIN_DIR" 2>/dev/null || true
150
+ echo "[install] drop-in 撤去(既定の ~/projects/$SITE に一致)"
151
+ fi
152
+
153
+ # --- 5) daemon-reload + enable --now -----------------------------------------
154
+ systemctl --user daemon-reload
155
+ echo "[install] daemon-reload 実行"
156
+
157
+ if systemctl --user enable --now "conductor@${SITE}.service"; then
158
+ echo "[install] enable --now: conductor@${SITE}"
159
+ else
160
+ fail "enable --now に失敗しました: conductor@${SITE}"
161
+ fi
162
+
163
+ # --- 6) linger(ログオフ中も常駐) -------------------------------------------
164
+ if loginctl enable-linger "$USER" 2>/dev/null; then
165
+ echo "[install] enable-linger: $USER(ログオフ中も常駐)"
166
+ else
167
+ warn "enable-linger に失敗しました。sudo が必要な場合: sudo loginctl enable-linger $USER"
168
+ fi
169
+
170
+ # --- 7) 起動状況の表示 -------------------------------------------------------
171
+ echo
172
+ echo "[install] 完了: conductor@${SITE} を有効化しました。"
173
+ echo
174
+ echo "--- 状態確認 ---"
175
+ systemctl --user --no-pager status "conductor@${SITE}.service" 2>/dev/null || true
176
+ echo
177
+ echo "ログ確認: journalctl --user -u conductor@${SITE} -f"
178
+ echo "停止: systemctl --user disable --now conductor@${SITE}.service"
179
+ echo "再起動: systemctl --user restart conductor@${SITE}.service"