@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
|
@@ -25,10 +25,24 @@ systemd が conductor@<プロジェクト> を自動起動
|
|
|
25
25
|
|
|
26
26
|
| ファイル | 役割 |
|
|
27
27
|
|----------|------|
|
|
28
|
-
| `
|
|
29
|
-
| `install.sh` |
|
|
28
|
+
| `install.sh` | 現場ごとの常駐のインストーラ(node検出→symlink→配置→enable→linger) |
|
|
29
|
+
| `paste-cache-prune-install.sh` | 貼り付け一時ファイルの毎日掃除タイマー(PCに1本) |
|
|
30
30
|
| `README.md` | このファイル |
|
|
31
31
|
|
|
32
|
+
ユニットファイルの**正本は配布物の `systemd/` に1つだけ**置いています(写しをここに持ちません。
|
|
33
|
+
2箇所に同じものがあると、片方だけ直したときに中身がズレるためです):
|
|
34
|
+
|
|
35
|
+
| ユニット | 本数 | 役割 | 入れる処理 |
|
|
36
|
+
|---|---|---|---|
|
|
37
|
+
| `systemd/conductor@.service` | 現場ごとに1本 | heartbeat + relay(指示の配達・完了検出) | このフォルダの `install.sh` |
|
|
38
|
+
| `systemd/conductor-ear.service` | **PCに1本** | 指示の着信合図を受け、各現場の relay を前倒しする「耳」 | `systemd/ear-install.sh`(`setup.sh` が自動で呼びます) |
|
|
39
|
+
|
|
40
|
+
`ExecStart` は配置時にインストーラが「その機で実際に動く `conductor` のフルパス」へ置換します。
|
|
41
|
+
ユニットをそのままコピーしても動きません(必ずインストーラ経由で入れてください)。
|
|
42
|
+
|
|
43
|
+
**耳が入らなくてもセットアップは止まりません。** 耳は「指示が届くまでの最大15秒の待ちを消す」加速装置で、
|
|
44
|
+
入らない機でも各現場の relay は15秒ごとの確認で従来どおり完全に動きます。
|
|
45
|
+
|
|
32
46
|
**conductor-orchestrator.service は含まない**: ADR-001 の決定により CC1B(LLM判断層)は撤去。orchestrator サービスは販売構成では不要。
|
|
33
47
|
|
|
34
48
|
## セットアップ手順
|
|
@@ -45,7 +59,7 @@ systemd が conductor@<プロジェクト> を自動起動
|
|
|
45
59
|
```bash
|
|
46
60
|
cat > "$HOME/.conductor.env" <<'EOF'
|
|
47
61
|
CONDUCTOR_URL=https://<采配くん管制サーバURL>
|
|
48
|
-
|
|
62
|
+
CONDUCTOR_TOKEN=<トークン>
|
|
49
63
|
EOF
|
|
50
64
|
chmod 600 "$HOME/.conductor.env"
|
|
51
65
|
```
|
|
@@ -118,7 +132,7 @@ install.sh はシェルで使っている node の実体パスを検出し、
|
|
|
118
132
|
|
|
119
133
|
| 項目 | 説明 | 例 |
|
|
120
134
|
|------|------|-----|
|
|
121
|
-
| `~/.conductor.env` | 管制サーバへの接続情報 | `CONDUCTOR_URL=https://...` / `
|
|
135
|
+
| `~/.conductor.env` | 管制サーバへの接続情報 | `CONDUCTOR_URL=https://...` / `CONDUCTOR_TOKEN=...` |
|
|
122
136
|
| プロジェクト名 | install.sh の第1引数 | `my-app` |
|
|
123
137
|
| プロジェクトディレクトリ | install.sh の第2引数(既定は `~/projects/<名前>`) | `/home/user/work/my-app` |
|
|
124
138
|
| sudo パスワード | symlink 作成時に必要(初回のみ) | — |
|
|
@@ -145,6 +159,16 @@ rm -rf ~/.config/systemd/user/conductor@my-app.service.d
|
|
|
145
159
|
systemctl --user daemon-reload
|
|
146
160
|
```
|
|
147
161
|
|
|
162
|
+
耳(PCに1本)はこちら:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
systemctl --user status conductor-ear # 状態確認
|
|
166
|
+
journalctl --user -u conductor-ear -f # ログ確認
|
|
167
|
+
systemctl --user restart conductor-ear # 再起動
|
|
168
|
+
systemctl --user disable --now conductor-ear # 停止(relay は15秒ごとの確認で動き続けます)
|
|
169
|
+
bash ../../systemd/ear-install.sh # 入れ直す
|
|
170
|
+
```
|
|
171
|
+
|
|
148
172
|
## トラブルシューティング
|
|
149
173
|
|
|
150
174
|
### サービスが起動しない
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
#
|
|
19
19
|
# 前提:
|
|
20
20
|
# - Linux で systemd が有効(WSL の場合: /etc/wsl.conf の [boot] systemd=true)。
|
|
21
|
-
# - ~/.conductor.env が用意済み(CONDUCTOR_URL /
|
|
21
|
+
# - ~/.conductor.env が用意済み(CONDUCTOR_URL / CONDUCTOR_TOKEN)。
|
|
22
22
|
# - @tyhld/conductor が npm グローバルインストール済み。
|
|
23
23
|
# - node がインストール済み(fnm / nvm / volta / システムパッケージ いずれか)。
|
|
24
24
|
# - sudo が使える(node symlink を /usr/local/bin に作成するため)。
|
|
@@ -51,8 +51,12 @@ command -v systemctl >/dev/null 2>&1 || \
|
|
|
51
51
|
systemctl --user show-environment >/dev/null 2>&1 || \
|
|
52
52
|
fail "systemd のユーザーセッションに接続できません(systemctl --user が不可)。"
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
# ★ユニットの正本は配布物の systemd/ に1つだけ(写しを持たない)。
|
|
55
|
+
# 以前は sales-template/systemd/ にも同じユニットの写しがあり、片方だけ直すと中身がズレた。
|
|
56
|
+
# 正本の在り処は「このスクリプトから見て ../../systemd」。npm 配布物でもリポジトリでも
|
|
57
|
+
# 同じ相対位置になる(package.json の files に systemd を含めてある)。
|
|
58
|
+
UNIT_SRC="$(cd "$SCRIPT_DIR/../.." && pwd)/systemd/conductor@.service"
|
|
59
|
+
[[ -f "$UNIT_SRC" ]] || fail "ユニットファイルがありません: $UNIT_SRC(配布物が壊れている可能性があります)"
|
|
56
60
|
|
|
57
61
|
[[ -d "$PROJECT_DIR" ]] || fail "プロジェクトディレクトリが存在しません: $PROJECT_DIR"
|
|
58
62
|
|
|
@@ -105,20 +109,46 @@ echo "[install] systemd PATH での node 解決: OK"
|
|
|
105
109
|
# npm グローバルインストール先は環境依存(~/.npm-global/bin, /usr/local/bin,
|
|
106
110
|
# ~/.volta/bin 等)で systemd PATH にない可能性がある。
|
|
107
111
|
# ユニット配置時に ExecStart にフルパスを書き込んで根治する。
|
|
112
|
+
#
|
|
113
|
+
# ★どちらを使うかの順番(2026-09-04 に直した見落とし)
|
|
114
|
+
# ① 開発機(~/projects/conductor/dist/cli.js が在る)… そのリポジトリの中身を使う
|
|
115
|
+
# ② お客さま機(無い) … npm で入れた全体版を使う
|
|
116
|
+
#
|
|
117
|
+
# 【なぜ①を先に見るか】以前は npm の全体版を先に掴んでいた。開発機で
|
|
118
|
+
# `npm i -g @tyhld/conductor` を一度でも打つと、常駐がリポジトリではなく
|
|
119
|
+
# 【配布済みの古い版】で動いてしまう。直したはずの不具合が現場で再現し続け、
|
|
120
|
+
# 原因を「直っていない」と読み違える(=いちばん質の悪い事故)。
|
|
121
|
+
# リポジトリが在る機体=開発機なので、そこではリポジトリを正とする。
|
|
122
|
+
REPO_CLI="$HOME/projects/conductor/dist/cli.js"
|
|
108
123
|
CONDUCTOR_BIN=""
|
|
109
|
-
|
|
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
|
|
110
134
|
CONDUCTOR_BIN="$(command -v conductor)"
|
|
111
135
|
[[ -L "$CONDUCTOR_BIN" ]] && CONDUCTOR_BIN="$(readlink -f "$CONDUCTOR_BIN")"
|
|
136
|
+
CONDUCTOR_SRC="npm で入れた全体版"
|
|
112
137
|
fi
|
|
113
138
|
|
|
114
139
|
if [[ -z "$CONDUCTOR_BIN" ]]; then
|
|
115
140
|
fail "conductor コマンドが見つかりません。先に npm i -g @tyhld/conductor を実行してください。"
|
|
116
141
|
fi
|
|
117
142
|
|
|
143
|
+
# ★どちらを使ったかを必ず1行出す(読み違えを防ぐ)。
|
|
144
|
+
echo "[install] 常駐が使う采配くん: ${CONDUCTOR_SRC}"
|
|
118
145
|
echo "[install] 検出した conductor: $CONDUCTOR_BIN"
|
|
119
146
|
|
|
120
147
|
# conductor が実際に動くか簡易検証
|
|
121
|
-
|
|
148
|
+
# ★開発機では CONDUCTOR_BIN が「node と cli.js の2語」になるので、語に割って呼ぶ。
|
|
149
|
+
# まとめて1語として呼ぶと、そんな名前のコマンドは無い=必ず失敗する。
|
|
150
|
+
read -r -a CONDUCTOR_CMD <<< "$CONDUCTOR_BIN"
|
|
151
|
+
if ! "${CONDUCTOR_CMD[@]}" --help >/dev/null 2>&1; then
|
|
122
152
|
warn "conductor コマンドの実行に失敗しました。インストールを確認してください。"
|
|
123
153
|
fi
|
|
124
154
|
|
|
@@ -127,9 +157,25 @@ UNIT_DIR="$HOME/.config/systemd/user"
|
|
|
127
157
|
UNIT_DST="$UNIT_DIR/conductor@.service"
|
|
128
158
|
mkdir -p "$UNIT_DIR"
|
|
129
159
|
|
|
160
|
+
# 台帳: 「このインストーラが入れたユニット」だけを記録し、uninstall.sh がそれを読んで消す。
|
|
161
|
+
# ユニット名を `conductor@*` で決め打ち走査しないため、`@` 無しの単発ユニットを取りこぼさない。
|
|
162
|
+
# 顧客が自作した conductor-*.service を巻き込まないのも台帳方式の利点。
|
|
163
|
+
LEDGER_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/conductor"
|
|
164
|
+
LEDGER_FILE="$LEDGER_DIR/installed-units.txt"
|
|
165
|
+
|
|
166
|
+
# 台帳へ1行追記する(冪等: 既にある行は重複させない)。
|
|
167
|
+
ledger_add() {
|
|
168
|
+
local unit="$1"
|
|
169
|
+
mkdir -p "$LEDGER_DIR"
|
|
170
|
+
touch "$LEDGER_FILE"
|
|
171
|
+
grep -qxF "$unit" "$LEDGER_FILE" || echo "$unit" >> "$LEDGER_FILE"
|
|
172
|
+
}
|
|
173
|
+
|
|
130
174
|
# テンプレートをコピーし、プレースホルダをフルパスに置換
|
|
131
175
|
sed "s|__CONDUCTOR_BIN__|${CONDUCTOR_BIN}|g" "$UNIT_SRC" > "$UNIT_DST"
|
|
132
176
|
echo "[install] 配置: $UNIT_DST (ExecStart=$CONDUCTOR_BIN)"
|
|
177
|
+
# テンプレート本体も台帳に載せる(撤去時にファイルを消す対象)。
|
|
178
|
+
ledger_add "conductor@.service"
|
|
133
179
|
|
|
134
180
|
# --- 4) WorkingDirectory の解決 -----------------------------------------------
|
|
135
181
|
DEFAULT_DIR="$HOME/projects/$SITE"
|
|
@@ -156,6 +202,8 @@ echo "[install] daemon-reload 実行"
|
|
|
156
202
|
|
|
157
203
|
if systemctl --user enable --now "conductor@${SITE}.service"; then
|
|
158
204
|
echo "[install] enable --now: conductor@${SITE}"
|
|
205
|
+
# enable に成功したものだけ台帳へ(入れていないものを撤去対象にしない)。
|
|
206
|
+
ledger_add "conductor@${SITE}.service"
|
|
159
207
|
else
|
|
160
208
|
fail "enable --now に失敗しました: conductor@${SITE}"
|
|
161
209
|
fi
|
|
@@ -177,3 +225,4 @@ echo
|
|
|
177
225
|
echo "ログ確認: journalctl --user -u conductor@${SITE} -f"
|
|
178
226
|
echo "停止: systemctl --user disable --now conductor@${SITE}.service"
|
|
179
227
|
echo "再起動: systemctl --user restart conductor@${SITE}.service"
|
|
228
|
+
echo "撤去: bash uninstall.sh --dry-run (確認後に --dry-run を外す)"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# paste-cache-prune-install.sh — 貼り付け一時ファイル(~/.claude/paste-cache)を
|
|
4
|
+
# 毎日自動で古い分だけ消す systemd --user タイマーを設置・有効化する。
|
|
5
|
+
#
|
|
6
|
+
# やること(すべて冪等):
|
|
7
|
+
# 1. tyhld-paste-cache-prune.service / .timer を ~/.config/systemd/user/ へコピー(上書き)。
|
|
8
|
+
# 2. systemctl --user daemon-reload。
|
|
9
|
+
# 3. systemctl --user enable --now tyhld-paste-cache-prune.timer。
|
|
10
|
+
# 4. loginctl enable-linger(ログオフ中も回るように。既に有効なら無害)。
|
|
11
|
+
# 5. 撤去用の台帳(installed-units.txt)へ登録(uninstall.sh がこれを読んで消す)。
|
|
12
|
+
#
|
|
13
|
+
# 安全:
|
|
14
|
+
# - 触るのは ~/.claude/paste-cache だけ(実際の削除はユニット側で範囲を固定している)。
|
|
15
|
+
# - systemd --user が使えない環境では警告して素通り(本体セットアップを止めない)。
|
|
16
|
+
#
|
|
17
|
+
# 使い方:
|
|
18
|
+
# bash paste-cache-prune-install.sh
|
|
19
|
+
#
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
|
|
22
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
UNIT_DIR="$HOME/.config/systemd/user"
|
|
24
|
+
SERVICE="tyhld-paste-cache-prune.service"
|
|
25
|
+
TIMER="tyhld-paste-cache-prune.timer"
|
|
26
|
+
|
|
27
|
+
# 撤去用の台帳(install.sh と同じ場所・同じ方式)。
|
|
28
|
+
LEDGER_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/conductor"
|
|
29
|
+
LEDGER_FILE="$LEDGER_DIR/installed-units.txt"
|
|
30
|
+
|
|
31
|
+
info() { echo "[paste-cache-prune] $*"; }
|
|
32
|
+
warn() { echo "[paste-cache-prune] 注意: $*" >&2; }
|
|
33
|
+
|
|
34
|
+
ledger_add() {
|
|
35
|
+
local unit="$1"
|
|
36
|
+
mkdir -p "$LEDGER_DIR"
|
|
37
|
+
touch "$LEDGER_FILE"
|
|
38
|
+
grep -qxF "$unit" "$LEDGER_FILE" || echo "$unit" >> "$LEDGER_FILE"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# ユニット正本がそろっているか(無ければ素通り)。
|
|
42
|
+
for u in "$SERVICE" "$TIMER"; do
|
|
43
|
+
if [[ ! -f "$SCRIPT_DIR/$u" ]]; then
|
|
44
|
+
warn "ユニットが見つかりません: $SCRIPT_DIR/$u(自動掃除タイマーはスキップ)"
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
# systemd --user が使えないなら素通り(本体を止めない)。
|
|
50
|
+
if ! command -v systemctl >/dev/null 2>&1 || ! systemctl --user show-environment >/dev/null 2>&1; then
|
|
51
|
+
warn "systemd --user が使えないため、自動掃除タイマーは設置しません。"
|
|
52
|
+
warn " (既存機では update-guard.sh の手動掃除が代わりに古い分を消します)"
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
mkdir -p "$UNIT_DIR"
|
|
57
|
+
cp -f "$SCRIPT_DIR/$SERVICE" "$UNIT_DIR/$SERVICE"
|
|
58
|
+
cp -f "$SCRIPT_DIR/$TIMER" "$UNIT_DIR/$TIMER"
|
|
59
|
+
info "配置: $UNIT_DIR/$SERVICE, $UNIT_DIR/$TIMER"
|
|
60
|
+
|
|
61
|
+
systemctl --user daemon-reload
|
|
62
|
+
|
|
63
|
+
if systemctl --user enable --now "$TIMER"; then
|
|
64
|
+
info "有効化: $TIMER(毎日1回・既定は 1 日より古い paste-cache を削除)"
|
|
65
|
+
ledger_add "$SERVICE"
|
|
66
|
+
ledger_add "$TIMER"
|
|
67
|
+
else
|
|
68
|
+
warn "enable --now に失敗しました: $TIMER"
|
|
69
|
+
exit 0
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# ログオフ中も回るように(他用途で既に有効なことも多い。失敗は握りつぶす)。
|
|
73
|
+
loginctl enable-linger "$USER" >/dev/null 2>&1 || true
|
|
74
|
+
|
|
75
|
+
systemctl --user --no-pager list-timers "$TIMER" 2>/dev/null || true
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# tyhld-paste-cache-prune.service — 画面貼り付けの一時ファイルを古い分だけ掃除する(1回実行)。
|
|
2
|
+
#
|
|
3
|
+
# なぜ要るか:
|
|
4
|
+
# Claude Code は長文を画面に貼り付けると、その本文を ~/.claude/paste-cache/<hash>.txt に
|
|
5
|
+
# 平文で退避する。送信後は本文が会話履歴側にも入るため、この一時ファイルは古くなれば不要。
|
|
6
|
+
# 放置すると平文の貼り付け(秘密を含み得る)が溜まり続けるので、古い分だけ自動で消す。
|
|
7
|
+
#
|
|
8
|
+
# 安全:
|
|
9
|
+
# - ★触るのは ~/.claude/paste-cache だけ。ほかのフォルダには一切触れない。
|
|
10
|
+
# - mtime 基準で「今日ぶん(未送信・参照中かもしれない分)」は残す=全消しはしない。
|
|
11
|
+
# - フォルダが無ければ何もしない。
|
|
12
|
+
#
|
|
13
|
+
# 消す範囲は PASTE_CACHE_KEEP_DAYS 日より古いファイル(既定 1)。環境変数で調整可。
|
|
14
|
+
# ・systemd の変数展開を避けるため、シェルへ渡す $ は $$ でエスケープしている。
|
|
15
|
+
# ・%h は systemd がユーザーのホームに展開する。
|
|
16
|
+
#
|
|
17
|
+
# .timer から1日1回呼ばれる(tyhld-paste-cache-prune.timer)。
|
|
18
|
+
|
|
19
|
+
[Unit]
|
|
20
|
+
Description=tyhld: prune old Claude Code paste-cache files (~/.claude/paste-cache)
|
|
21
|
+
|
|
22
|
+
[Service]
|
|
23
|
+
Type=oneshot
|
|
24
|
+
Environment=PASTE_CACHE_KEEP_DAYS=1
|
|
25
|
+
ExecStart=/bin/sh -c 'd="%h/.claude/paste-cache"; [ -d "$$d" ] || exit 0; find "$$d" -mindepth 1 -maxdepth 1 -type f -mtime +$${PASTE_CACHE_KEEP_DAYS:-1} -delete'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# tyhld-paste-cache-prune.timer — 上の掃除サービスを毎日1回起動する。
|
|
2
|
+
#
|
|
3
|
+
# 顧客PCは update-guard.sh を手で回さないため、この常時タイマーが恒久策の本体。
|
|
4
|
+
# - OnCalendar=daily … 毎日1回。
|
|
5
|
+
# - Persistent=true … PCが消えていて実行を取りこぼした日は、次回起動直後に1度だけ追い実行。
|
|
6
|
+
# - RandomizedDelaySec … 起動直後に集中しないよう少しばらす。
|
|
7
|
+
#
|
|
8
|
+
# enable --now は setup.sh(新規機)と update-guard.sh(既存機・APPLY時)の両方が行う(冪等)。
|
|
9
|
+
|
|
10
|
+
[Unit]
|
|
11
|
+
Description=tyhld: daily prune of Claude Code paste-cache
|
|
12
|
+
|
|
13
|
+
[Timer]
|
|
14
|
+
OnCalendar=daily
|
|
15
|
+
Persistent=true
|
|
16
|
+
RandomizedDelaySec=15min
|
|
17
|
+
|
|
18
|
+
[Install]
|
|
19
|
+
WantedBy=timers.target
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# uninstall.sh — 采配くんを顧客PCから撤去する(systemd 常駐の停止・無効化・ユニット削除)。
|
|
4
|
+
#
|
|
5
|
+
# 【なぜこのファイルが sales-template にあるか】
|
|
6
|
+
# 撤去スクリプトは従来 scripts/ にしか無く、npm パッケージの files に scripts が含まれないため
|
|
7
|
+
# 顧客の手元に一切届いていなかった(2026-07-09 調査)。撤去手段は配布物に同梱されて初めて意味を持つ。
|
|
8
|
+
#
|
|
9
|
+
# 【台帳方式】インストーラが ~/.config/conductor/installed-units.txt に「自分が入れたユニット」を
|
|
10
|
+
# 記録する。ここではそれを読んで消す。ユニット名を `conductor@*` で決め打ち走査しないため、
|
|
11
|
+
# `@` を含まない単発ユニット(旧 conductor-orchestrator.service 等)も取りこぼさず、
|
|
12
|
+
# かつ顧客が自作した無関係な conductor-*.service を巻き込まない。
|
|
13
|
+
#
|
|
14
|
+
# 【順序は不変】disable --now → ファイル削除 → daemon-reload。
|
|
15
|
+
# 先にファイルを消すと disable が WantedBy の symlink を残す。
|
|
16
|
+
#
|
|
17
|
+
# 使い方:
|
|
18
|
+
# bash uninstall.sh # 確認プロンプトあり
|
|
19
|
+
# bash uninstall.sh --yes # 確認せず実行
|
|
20
|
+
# bash uninstall.sh --dry-run # 何を消すか表示するだけ(何も変更しない)
|
|
21
|
+
#
|
|
22
|
+
# このスクリプトが「やらない」こと(意図的。案内のみ出す):
|
|
23
|
+
# - loginctl disable-linger … linger は他用途でも使われ得るため外さない
|
|
24
|
+
# - ~/.conductor.env の削除 … 接続情報(秘密)を含むため消さない
|
|
25
|
+
# - npm -g @tyhld/conductor の削除 … パッケージ管理は利用者の領分
|
|
26
|
+
#
|
|
27
|
+
set -euo pipefail
|
|
28
|
+
|
|
29
|
+
UNIT_DIR="$HOME/.config/systemd/user"
|
|
30
|
+
LEDGER_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/conductor"
|
|
31
|
+
LEDGER_FILE="$LEDGER_DIR/installed-units.txt"
|
|
32
|
+
|
|
33
|
+
# ★Mac(launchd)は仕組みが違うので、そちらの撤去を先に済ませて終わる(ADR-019)。
|
|
34
|
+
# 台帳 installed-launchd.txt に「入れた LaunchAgent の名前」が入っている。
|
|
35
|
+
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
36
|
+
MAC_LEDGER="$LEDGER_DIR/installed-launchd.txt"
|
|
37
|
+
AGENT_DIR="$HOME/Library/LaunchAgents"
|
|
38
|
+
MAC_DRY=0
|
|
39
|
+
MAC_YES=0
|
|
40
|
+
for a in "$@"; do
|
|
41
|
+
case "$a" in --dry-run) MAC_DRY=1 ;; --yes|-y) MAC_YES=1 ;; esac
|
|
42
|
+
done
|
|
43
|
+
if [[ ! -f "$MAC_LEDGER" ]]; then
|
|
44
|
+
echo "撤去対象はありません(台帳が無い=采配くんの常駐は入っていないようです)。"
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
47
|
+
echo "撤去する常駐(launchd):"
|
|
48
|
+
while IFS= read -r label || [[ -n "$label" ]]; do
|
|
49
|
+
[[ -z "$label" ]] && continue
|
|
50
|
+
echo " - $label"
|
|
51
|
+
done < "$MAC_LEDGER"
|
|
52
|
+
if [[ $MAC_DRY -eq 1 ]]; then
|
|
53
|
+
echo "(--dry-run のため何も変更していません)"
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
56
|
+
if [[ $MAC_YES -ne 1 ]]; then
|
|
57
|
+
read -r -p "撤去してよろしいですか (y/N): " ans
|
|
58
|
+
[[ "$(printf '%s' "$ans" | tr '[:upper:]' '[:lower:]')" == "y" ]] || { echo "中止しました。"; exit 0; }
|
|
59
|
+
fi
|
|
60
|
+
while IFS= read -r label || [[ -n "$label" ]]; do
|
|
61
|
+
[[ -z "$label" ]] && continue
|
|
62
|
+
launchctl bootout "gui/$(id -u)/${label}" >/dev/null 2>&1 || \
|
|
63
|
+
launchctl unload "$AGENT_DIR/${label}.plist" >/dev/null 2>&1 || true
|
|
64
|
+
rm -f "$AGENT_DIR/${label}.plist"
|
|
65
|
+
echo " 撤去: $label"
|
|
66
|
+
done < "$MAC_LEDGER"
|
|
67
|
+
rm -f "$MAC_LEDGER"
|
|
68
|
+
echo "撤去しました。"
|
|
69
|
+
echo "★消していないもの(意図的): ~/.conductor.env(接続情報)/番人一式(~/.tyhld)/npm の采配くん本体"
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
ASSUME_YES=0
|
|
74
|
+
DRY_RUN=0
|
|
75
|
+
for arg in "$@"; do
|
|
76
|
+
case "$arg" in
|
|
77
|
+
--yes|-y) ASSUME_YES=1 ;;
|
|
78
|
+
--dry-run) DRY_RUN=1 ;;
|
|
79
|
+
-h|--help)
|
|
80
|
+
echo "使い方: bash uninstall.sh [--yes] [--dry-run]"
|
|
81
|
+
exit 0
|
|
82
|
+
;;
|
|
83
|
+
*)
|
|
84
|
+
echo "エラー: 不明な引数: ${arg}" >&2
|
|
85
|
+
exit 1
|
|
86
|
+
;;
|
|
87
|
+
esac
|
|
88
|
+
done
|
|
89
|
+
|
|
90
|
+
if ! command -v systemctl >/dev/null 2>&1; then
|
|
91
|
+
echo "エラー: systemctl が見つかりません。systemd が有効か確認してください。" >&2
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
if ! systemctl --user show-environment >/dev/null 2>&1; then
|
|
95
|
+
echo "エラー: systemd のユーザーセッションに接続できません(systemctl --user が不可)。" >&2
|
|
96
|
+
exit 1
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# --- 1) 撤去対象の決定(台帳 → 無ければ従来 glob へフォールバック) -----------
|
|
100
|
+
targets=()
|
|
101
|
+
if [[ -f "$LEDGER_FILE" ]]; then
|
|
102
|
+
while IFS= read -r raw || [[ -n "$raw" ]]; do
|
|
103
|
+
line="${raw#"${raw%%[![:space:]]*}"}"
|
|
104
|
+
line="${line%"${line##*[![:space:]]}"}"
|
|
105
|
+
[[ -z "$line" || "${line:0:1}" == "#" ]] && continue
|
|
106
|
+
targets+=("$line")
|
|
107
|
+
done < "$LEDGER_FILE"
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
USED_FALLBACK=0
|
|
111
|
+
if [[ ${#targets[@]} -eq 0 ]]; then
|
|
112
|
+
# 台帳導入前にインストールされた環境を救う。`@` 無しの単発ユニットも対象に含める。
|
|
113
|
+
USED_FALLBACK=1
|
|
114
|
+
echo "警告: 台帳が見つかりません(${LEDGER_FILE})。従来の走査へフォールバックします。" >&2
|
|
115
|
+
echo "警告: conductor@*.service と conductor-*.service を対象にします。" >&2
|
|
116
|
+
echo "警告: 自作の conductor-*.service があれば巻き込む恐れがあります。まず --dry-run で確認してください。" >&2
|
|
117
|
+
# ★mapfile は bash 4 以降。Mac 標準の bash 3.2 でも動くよう while read で読む(ADR-019)。
|
|
118
|
+
while IFS= read -r _line; do
|
|
119
|
+
[[ -n "$_line" ]] && targets+=("$_line")
|
|
120
|
+
done < <(
|
|
121
|
+
{
|
|
122
|
+
systemctl --user list-unit-files --no-legend 'conductor@*.service' 2>/dev/null | awk '{print $1}'
|
|
123
|
+
systemctl --user list-units --no-legend --all 'conductor@*.service' 2>/dev/null | awk '{print $1}'
|
|
124
|
+
systemctl --user list-unit-files --no-legend 'conductor-*.service' 2>/dev/null | awk '{print $1}'
|
|
125
|
+
systemctl --user list-units --no-legend --all 'conductor-*.service' 2>/dev/null | awk '{print $1}'
|
|
126
|
+
} | sed '/^$/d' | sort -u
|
|
127
|
+
)
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# ★連想配列(declare -A)は bash 4 以降。3.2 でも動くよう、見た名前を1行ずつ持って照合する。
|
|
131
|
+
seen_list=""
|
|
132
|
+
uniq_targets=()
|
|
133
|
+
for u in "${targets[@]}"; do
|
|
134
|
+
[[ -z "$u" ]] && continue
|
|
135
|
+
case "
|
|
136
|
+
$seen_list" in
|
|
137
|
+
*"
|
|
138
|
+
$u
|
|
139
|
+
"*) continue ;;
|
|
140
|
+
esac
|
|
141
|
+
seen_list="$seen_list$u
|
|
142
|
+
"
|
|
143
|
+
uniq_targets+=("$u")
|
|
144
|
+
done
|
|
145
|
+
|
|
146
|
+
# drop-in ディレクトリも列挙(表示と削除で使う)。
|
|
147
|
+
shopt -s nullglob
|
|
148
|
+
dropins=("$UNIT_DIR"/conductor@*.service.d)
|
|
149
|
+
shopt -u nullglob
|
|
150
|
+
|
|
151
|
+
if [[ ${#uniq_targets[@]} -eq 0 && ${#dropins[@]} -eq 0 ]]; then
|
|
152
|
+
echo "撤去対象はありません。采配くんの systemd 常駐は既に入っていないようです。"
|
|
153
|
+
exit 0
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# --- 2) 何を消すかを必ず列挙する --------------------------------------------
|
|
157
|
+
echo
|
|
158
|
+
echo "以下を撤去します:"
|
|
159
|
+
echo " 台帳: ${LEDGER_FILE}$([[ $USED_FALLBACK -eq 1 ]] && echo ' (不在→フォールバック走査)')"
|
|
160
|
+
for u in "${uniq_targets[@]}"; do
|
|
161
|
+
if [[ "$u" == "conductor@.service" ]]; then
|
|
162
|
+
echo " - ${u}(ファイル削除のみ/テンプレート本体)"
|
|
163
|
+
else
|
|
164
|
+
echo " - ${u}(disable --now → ファイル削除)"
|
|
165
|
+
fi
|
|
166
|
+
done
|
|
167
|
+
for d in "${dropins[@]}"; do
|
|
168
|
+
echo " - $(basename "$d")(drop-in ディレクトリ削除)"
|
|
169
|
+
done
|
|
170
|
+
echo " 最後に daemon-reload を1回実行します。"
|
|
171
|
+
echo
|
|
172
|
+
echo "消さないもの(必要なら手動で):"
|
|
173
|
+
echo " - linger 設定 : loginctl disable-linger \"\$USER\""
|
|
174
|
+
echo " - 接続情報 : \$HOME/.conductor.env (秘密を含むため残します)"
|
|
175
|
+
echo " - npm パッケージ : npm uninstall -g @tyhld/conductor"
|
|
176
|
+
echo
|
|
177
|
+
|
|
178
|
+
if [[ $DRY_RUN -eq 1 ]]; then
|
|
179
|
+
echo "--dry-run のため、ここで終了します(何も変更していません)。"
|
|
180
|
+
exit 0
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
if [[ $ASSUME_YES -ne 1 ]]; then
|
|
184
|
+
read -r -p "本当に撤去しますか? [y/N]: " answer
|
|
185
|
+
case "$answer" in
|
|
186
|
+
y|Y|yes|YES) ;;
|
|
187
|
+
*) echo "中止しました。"; exit 0 ;;
|
|
188
|
+
esac
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# --- 3) disable --now(失敗は握りつぶす=冪等。既に無効でも進む) -------------
|
|
192
|
+
disabled=()
|
|
193
|
+
for u in "${uniq_targets[@]}"; do
|
|
194
|
+
[[ "$u" == "conductor@.service" ]] && continue # テンプレート本体は起動単位ではない
|
|
195
|
+
if systemctl --user disable --now "$u" 2>/dev/null; then
|
|
196
|
+
echo "[uninstall] disable --now: ${u}"
|
|
197
|
+
disabled+=("$u")
|
|
198
|
+
else
|
|
199
|
+
echo "[uninstall] warn: disable に失敗(既に無効かもしれません): ${u}" >&2
|
|
200
|
+
fi
|
|
201
|
+
done
|
|
202
|
+
|
|
203
|
+
# --- 4) ユニットファイル+drop-in を削除(★disable の後) --------------------
|
|
204
|
+
removed=()
|
|
205
|
+
for u in "${uniq_targets[@]}"; do
|
|
206
|
+
if [[ -f "$UNIT_DIR/$u" ]]; then
|
|
207
|
+
rm -f "$UNIT_DIR/$u"
|
|
208
|
+
removed+=("$u")
|
|
209
|
+
fi
|
|
210
|
+
done
|
|
211
|
+
if [[ -f "$UNIT_DIR/conductor@.service" ]]; then
|
|
212
|
+
rm -f "$UNIT_DIR/conductor@.service"
|
|
213
|
+
removed+=("conductor@.service")
|
|
214
|
+
fi
|
|
215
|
+
for d in "${dropins[@]}"; do
|
|
216
|
+
rm -rf "$d"
|
|
217
|
+
removed+=("$(basename "$d")")
|
|
218
|
+
done
|
|
219
|
+
|
|
220
|
+
systemctl --user daemon-reload
|
|
221
|
+
echo "[uninstall] daemon-reload 実行"
|
|
222
|
+
|
|
223
|
+
# --- 5) 台帳の後始末(撤去できたものを消す。空なら台帳ごと削除) --------------
|
|
224
|
+
if [[ -f "$LEDGER_FILE" ]]; then
|
|
225
|
+
survivors=()
|
|
226
|
+
for u in "${uniq_targets[@]}"; do
|
|
227
|
+
[[ -f "$UNIT_DIR/$u" ]] && survivors+=("$u")
|
|
228
|
+
done
|
|
229
|
+
if [[ ${#survivors[@]} -eq 0 ]]; then
|
|
230
|
+
rm -f "$LEDGER_FILE"
|
|
231
|
+
rmdir "$LEDGER_DIR" 2>/dev/null || true
|
|
232
|
+
echo "[uninstall] 台帳を削除(全て撤去済み): ${LEDGER_FILE}"
|
|
233
|
+
else
|
|
234
|
+
printf '%s\n' "${survivors[@]}" > "$LEDGER_FILE"
|
|
235
|
+
echo "[uninstall] warn: 撤去できなかった ${#survivors[@]} 件を台帳に残しました: ${survivors[*]}" >&2
|
|
236
|
+
fi
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
# --- 6) まとめ --------------------------------------------------------------
|
|
240
|
+
echo
|
|
241
|
+
echo "[uninstall] 完了。"
|
|
242
|
+
echo " 無効化: ${#disabled[@]}件${disabled[*]:+ — ${disabled[*]}}"
|
|
243
|
+
echo " 削除: ${#removed[@]}件${removed[*]:+ — ${removed[*]}}"
|
|
244
|
+
echo
|
|
245
|
+
echo "残したもの: linger 設定 / \$HOME/.conductor.env / npm パッケージ(上の案内を参照)"
|