@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,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tyhld/conductor — 耳が使う WebSocket 実装の選び方(ADR-066 / 実機 Node 20 での不具合の根治)。
|
|
3
|
+
*
|
|
4
|
+
* 【何が起きていたか】
|
|
5
|
+
* supabase-js は WebSocket を「実行環境の標準品(globalThis.WebSocket)」から探す。これが標準で
|
|
6
|
+
* 入ったのは Node 22 からで、実機の Node 20 では見つからず
|
|
7
|
+
* 「Node.js detected but native WebSocket not found.」
|
|
8
|
+
* で購読の開始に失敗し続けた(バックオフ再接続とポーリング肩代わりは効いていたので実害は無いが、
|
|
9
|
+
* 耳が一度も張れない=加速がまるごと効かない)。
|
|
10
|
+
*
|
|
11
|
+
* 【なぜ「Nodeを22へ上げる」で直さないか】
|
|
12
|
+
* 采配くんはお客さまのPCで動く商品で、そのPCの Node の版はこちらで決められない。
|
|
13
|
+
* 「Nodeを上げてください」を導入条件にすると、上げられない機では永久に耳が生えない。
|
|
14
|
+
* だから WebSocket の実装そのものをこのリポに同梱し、どの Node(18/20/22) でも耳が張れるようにする。
|
|
15
|
+
* =環境に合わせて商品が動く形にする(商品に合わせて環境を直させない)。
|
|
16
|
+
*
|
|
17
|
+
* 【選び方】
|
|
18
|
+
* ① 実行環境に標準の WebSocket があればそれを使う(Node 22+ / ブラウザ)。
|
|
19
|
+
* ② 無ければ同梱の ws パッケージを使い、supabase-js の transport オプションへ渡す。
|
|
20
|
+
* ③ どちらも用意できなければ耳は張らない——ただし【起動不能にはしない】。ログを残して諦め、
|
|
21
|
+
* 各 relay の15秒ポーリングでこれまでどおり動かす(便2b以来の作法)。
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* 実行環境の標準 WebSocket を返す純粋関数(無ければ null)。
|
|
25
|
+
* scope を引数にしてあるのは、標準品が「ある機/ない機」の両方をテストで作れるようにするため。
|
|
26
|
+
*/
|
|
27
|
+
export function pickNativeWebSocket(scope = globalThis) {
|
|
28
|
+
const ws = scope.WebSocket;
|
|
29
|
+
return typeof ws === 'function' ? ws : null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 読み込んだ ws パッケージの形から、コンストラクタを取り出す純粋関数。
|
|
33
|
+
* ws は既定書き出し(default)でもあり名前付き(WebSocket)でもあるので、どちらの形でも拾う。
|
|
34
|
+
* 取り出せなければ null(=同梱品が壊れている/形が変わった。耳は張らずポーリングに任せる)。
|
|
35
|
+
*/
|
|
36
|
+
export function pickBundledWebSocket(mod) {
|
|
37
|
+
if (mod === null || typeof mod !== 'object')
|
|
38
|
+
return null;
|
|
39
|
+
const m = mod;
|
|
40
|
+
const candidate = typeof m.default === 'function' ? m.default : m.WebSocket;
|
|
41
|
+
return typeof candidate === 'function' ? candidate : null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 耳が使う WebSocket 実装を決める。標準品 → 同梱品 の順で探し、どちらも無ければ null。
|
|
45
|
+
* 同梱品は動的 import で読む。npm install を忘れた機でも耳のプロセスごと落とさないため
|
|
46
|
+
* (読めなければ「耳なし=ポーリングのみ」で静かに動き続けるのが正しい)。
|
|
47
|
+
*/
|
|
48
|
+
export async function resolveWebSocketTransport(scope = globalThis) {
|
|
49
|
+
const native = pickNativeWebSocket(scope);
|
|
50
|
+
if (native !== null)
|
|
51
|
+
return { kind: 'native', ctor: native };
|
|
52
|
+
try {
|
|
53
|
+
const mod = await import('ws');
|
|
54
|
+
const bundled = pickBundledWebSocket(mod);
|
|
55
|
+
return bundled === null ? null : { kind: 'bundled', ctor: bundled };
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null; // 同梱品が入っていない(npm install 未実施 等)。
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** ログ用の言い回し(何を使って繋いでいるかが1行で分かるように)。 */
|
|
62
|
+
export function describeTransport(transport) {
|
|
63
|
+
return transport.kind === 'native'
|
|
64
|
+
? 'この機の標準WebSocketを使います'
|
|
65
|
+
: '同梱のWebSocket部品(ws)を使います(この機のNodeには標準のWebSocketがないため)';
|
|
66
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# ear-install.sh(launchd 版)— Mac に「耳」(conductor ear) を1本だけ設置する。★ADR-019
|
|
4
|
+
#
|
|
5
|
+
# ★これは systemd/ear-install.sh の「Mac 版の相方」です。役割はまったく同じで、
|
|
6
|
+
# 指示が積まれた合図を受け取り、その現場の relay へ「今すぐ1周して」と伝えます(PCに1本)。
|
|
7
|
+
#
|
|
8
|
+
# 使い方:
|
|
9
|
+
# bash launchd/ear-install.sh
|
|
10
|
+
#
|
|
11
|
+
# ★best-effort: 入らなくてもセットアップは止めない。耳は加速装置であって依存先ではなく、
|
|
12
|
+
# 各現場の relay は15秒ごとの確認でこれまでどおり完全に動きます(systemd 版と同じ方針)。
|
|
13
|
+
#
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
fail() { echo "[launchd-ear] エラー: $1" >&2; exit 1; }
|
|
17
|
+
warn() { echo "[launchd-ear] warn: $1" >&2; }
|
|
18
|
+
|
|
19
|
+
LABEL="com.tyhld.conductor.ear"
|
|
20
|
+
|
|
21
|
+
[[ "$(uname -s)" == "Darwin" ]] || fail "これは Mac 用です。Linux/WSL では systemd/ear-install.sh を使います。"
|
|
22
|
+
command -v launchctl >/dev/null 2>&1 || fail "launchctl が見つかりません。"
|
|
23
|
+
|
|
24
|
+
# 鍵を読んでから起動する入れ物は、現場ごとの install.sh が作る同じものを使い回す。
|
|
25
|
+
# ★耳だけを単体で入れたときのために、無ければここでも作る(作り方は1か所に寄せたいが、
|
|
26
|
+
# 耳は「PCに1本」で現場に依存しないため、同じ内容を同じ場所に置く=結果は同一)。
|
|
27
|
+
RUNNER="$HOME/.tyhld/bin/conductor-run.sh"
|
|
28
|
+
if [[ ! -x "$RUNNER" ]]; then
|
|
29
|
+
resolve_path() {
|
|
30
|
+
python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$1" 2>/dev/null || printf '%s' "$1"
|
|
31
|
+
}
|
|
32
|
+
command -v node >/dev/null 2>&1 || fail "node が見つかりません。"
|
|
33
|
+
command -v conductor >/dev/null 2>&1 || fail "conductor が見つかりません。"
|
|
34
|
+
NODE_BIN="$(resolve_path "$(command -v node)")"
|
|
35
|
+
CONDUCTOR_JS="$(resolve_path "$(command -v conductor)")"
|
|
36
|
+
mkdir -p "$(dirname "$RUNNER")"
|
|
37
|
+
cat > "$RUNNER" <<RUNEOF
|
|
38
|
+
#!/bin/bash
|
|
39
|
+
# 采配くんの常駐を起動する入れ物(launchd 用・ear-install.sh が生成)。
|
|
40
|
+
set -eu
|
|
41
|
+
if [ -f "\$HOME/.conductor.env" ]; then
|
|
42
|
+
set -a
|
|
43
|
+
. "\$HOME/.conductor.env"
|
|
44
|
+
set +a
|
|
45
|
+
fi
|
|
46
|
+
exec "$NODE_BIN" "$CONDUCTOR_JS" "\$@"
|
|
47
|
+
RUNEOF
|
|
48
|
+
chmod 700 "$RUNNER"
|
|
49
|
+
echo "[launchd-ear] 起動の入れ物を配置: $RUNNER"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
AGENT_DIR="$HOME/Library/LaunchAgents"
|
|
53
|
+
PLIST="$AGENT_DIR/${LABEL}.plist"
|
|
54
|
+
LOG_DIR="$HOME/Library/Logs/tyhld"
|
|
55
|
+
mkdir -p "$AGENT_DIR" "$LOG_DIR"
|
|
56
|
+
|
|
57
|
+
cat > "$PLIST" <<PLISTEOF
|
|
58
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
59
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
60
|
+
<plist version="1.0">
|
|
61
|
+
<dict>
|
|
62
|
+
<key>Label</key><string>${LABEL}</string>
|
|
63
|
+
<key>ProgramArguments</key>
|
|
64
|
+
<array>
|
|
65
|
+
<string>${RUNNER}</string>
|
|
66
|
+
<string>ear</string>
|
|
67
|
+
</array>
|
|
68
|
+
<key>WorkingDirectory</key><string>${HOME}</string>
|
|
69
|
+
<key>RunAtLoad</key><true/>
|
|
70
|
+
<key>KeepAlive</key><true/>
|
|
71
|
+
<key>ThrottleInterval</key><integer>10</integer>
|
|
72
|
+
<key>StandardOutPath</key><string>${LOG_DIR}/conductor-ear.log</string>
|
|
73
|
+
<key>StandardErrorPath</key><string>${LOG_DIR}/conductor-ear.log</string>
|
|
74
|
+
</dict>
|
|
75
|
+
</plist>
|
|
76
|
+
PLISTEOF
|
|
77
|
+
echo "[launchd-ear] 配置: $PLIST"
|
|
78
|
+
|
|
79
|
+
UID_NUM="$(id -u)"
|
|
80
|
+
launchctl bootout "gui/${UID_NUM}/${LABEL}" >/dev/null 2>&1 || true
|
|
81
|
+
if launchctl bootstrap "gui/${UID_NUM}" "$PLIST" >/dev/null 2>&1; then
|
|
82
|
+
echo "[launchd-ear] 起動しました: $LABEL"
|
|
83
|
+
else
|
|
84
|
+
launchctl unload "$PLIST" >/dev/null 2>&1 || true
|
|
85
|
+
launchctl load -w "$PLIST" >/dev/null 2>&1 \
|
|
86
|
+
&& echo "[launchd-ear] 起動しました(load 方式): $LABEL" \
|
|
87
|
+
|| warn "起動に失敗しました(耳が無くても指示は15秒以内に届きます)"
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
LEDGER_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/conductor"
|
|
91
|
+
LEDGER_FILE="$LEDGER_DIR/installed-launchd.txt"
|
|
92
|
+
mkdir -p "$LEDGER_DIR"
|
|
93
|
+
touch "$LEDGER_FILE"
|
|
94
|
+
grep -qxF "$LABEL" "$LEDGER_FILE" || echo "$LABEL" >> "$LEDGER_FILE"
|
|
95
|
+
|
|
96
|
+
echo "[launchd-ear] 完了(このPCに1本)"
|
package/package.json
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tyhld/conductor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "采配くん管制の見守りアプリ(conductor-agent)。各PCで常駐し、中央(devlog-tracker)へ定期的に生存報告(heartbeat)を送る常駐CLI。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"conductor": "dist/cli.js"
|
|
8
8
|
},
|
|
9
|
+
"_files_note": [
|
|
10
|
+
"★ここはお客さまへ届く中身そのもの(ADR-018)。1ファイルずつ挙げると必ず取りこぼす。",
|
|
11
|
+
" 実際 2026-09-02 まで scripts/hooks/cc2_guard.py だけを挙げていたため、setup.sh が要求する",
|
|
12
|
+
" 番人6本のうち5本が同梱されず、npm で配ると設置が⑤で必ず止まる状態だった。",
|
|
13
|
+
"★だからフォルダごと挙げる。新しい番人を足しても自動で同梱される=取りこぼしが起こせない。",
|
|
14
|
+
"★同梱物は test/package-contents.test.ts が npm pack の実物を見て検査する",
|
|
15
|
+
" (setup.sh が要る物が全部入っているか/秘密が混ざっていないか)。"
|
|
16
|
+
],
|
|
9
17
|
"files": [
|
|
10
18
|
"dist",
|
|
11
19
|
"sales-template",
|
|
20
|
+
"scripts/hooks",
|
|
21
|
+
"!scripts/hooks/__pycache__",
|
|
22
|
+
"skills",
|
|
23
|
+
"launchd",
|
|
24
|
+
"systemd",
|
|
12
25
|
"README.md"
|
|
13
26
|
],
|
|
14
27
|
"engines": {
|
|
@@ -20,7 +33,18 @@
|
|
|
20
33
|
"test": "tsc && node --test --experimental-strip-types test/*.test.ts",
|
|
21
34
|
"prepublishOnly": "npm run build"
|
|
22
35
|
},
|
|
36
|
+
"_publishConfig_note": [
|
|
37
|
+
"★配布先は公開 npm(registry.npmjs.org)1本(えふさん決定 2026-09-04)。",
|
|
38
|
+
" 実測 2026-09-04: 公開 npm には 0.3.0 しか無く、0.5.0 は GitHub Packages(非公開)に出ていた。",
|
|
39
|
+
" 原因は publish 時に手元の ~/.npmrc の @tyhld スコープ設定(GitHub Packages 向き)が効くこと。",
|
|
40
|
+
" お客さまは公開 npm の 0.3.0 を掴む。0.3.0 の dist は古い鍵名 CONDUCTOR_SHARED_SECRET を読むので、",
|
|
41
|
+
" いまの接続情報(CONDUCTOR_TOKEN)では『未設定です』で落ちる(migakia の設置で再現)。",
|
|
42
|
+
"★registry を書いておくと、publish のときだけ効き、.npmrc のスコープ設定より優先される。",
|
|
43
|
+
" =手元の設定が何であっても公開 npm へ出る。取得側(他リポの @tyhld/* 部品)は一切変えない。",
|
|
44
|
+
"★このキーは1つだけ。2つ書くと後ろが勝ち、前に書いたほうは黙って無視される(実際に踏んだ)。"
|
|
45
|
+
],
|
|
23
46
|
"publishConfig": {
|
|
47
|
+
"registry": "https://registry.npmjs.org",
|
|
24
48
|
"access": "public"
|
|
25
49
|
},
|
|
26
50
|
"repository": {
|
|
@@ -30,6 +54,11 @@
|
|
|
30
54
|
"license": "MIT",
|
|
31
55
|
"devDependencies": {
|
|
32
56
|
"@types/node": "^25.9.1",
|
|
57
|
+
"@types/ws": "^8.18.1",
|
|
33
58
|
"typescript": "^6.0.3"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@supabase/supabase-js": "^2.112.4",
|
|
62
|
+
"ws": "^8.21.3"
|
|
34
63
|
}
|
|
35
64
|
}
|
package/sales-template/README.md
CHANGED
|
@@ -2,23 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
采配くんを顧客PCにインストールするためのテンプレート一式です。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## クイックスタート(★お客さまが打つのはこの2行だけ)
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
npm i -g @tyhld/conductor # 1) 采配くんを入れる
|
|
9
|
+
conductor setup my-app # 2) 設置する(現場名を指定)
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
+
`conductor setup` は、配布物に同梱されたインストーラ(`sales-template/setup.sh`)を呼ぶ入口です。
|
|
13
|
+
**ファイルの場所を知らなくても設置できます**(ADR-018)。
|
|
14
|
+
|
|
15
|
+
- 現場のフォルダを別の場所に置いている場合: `conductor setup my-app /path/to/my-app`
|
|
16
|
+
- 設置のあとに聞かれるのは **管制のアドレス**と**合言葉**の2つだけ(どちらも弊社からお渡しします)。
|
|
17
|
+
★合言葉は入力しても画面には表示されません。
|
|
18
|
+
|
|
19
|
+
<details><summary>ファイルを直接指定して実行する場合(上級者向け)</summary>
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bash "$(npm root -g)/@tyhld/conductor/sales-template/setup.sh" my-app /path/to/my-app
|
|
23
|
+
```
|
|
24
|
+
</details>
|
|
25
|
+
|
|
26
|
+
### 対応している OS
|
|
27
|
+
|
|
28
|
+
| OS | 状態 |
|
|
29
|
+
|---|---|
|
|
30
|
+
| **Windows**(WSL2 + Ubuntu) | 対応(常駐は systemd) |
|
|
31
|
+
| **Mac**(macOS) | ★**対応したが実機未検証**(常駐は launchd・ADR-019)。ログイン中だけ動きます |
|
|
32
|
+
|
|
12
33
|
### 必要なもの
|
|
13
34
|
|
|
14
35
|
| 項目 | 説明 |
|
|
15
36
|
|------|------|
|
|
16
|
-
| Node.js 18+ |
|
|
17
|
-
| tmux | `sudo apt install tmux` |
|
|
18
|
-
| git | `sudo apt install git` |
|
|
19
|
-
| Python 3.8+ | OS
|
|
20
|
-
|
|
|
21
|
-
| sudo | node
|
|
37
|
+
| Node.js 18+ | nvm / fnm / volta、または `sudo apt install` / `brew install` |
|
|
38
|
+
| tmux | Windows: `sudo apt install tmux` / Mac: `brew install tmux` |
|
|
39
|
+
| git | Windows: `sudo apt install git` / Mac: Xcode コマンドラインツールに同梱 |
|
|
40
|
+
| Python 3.8+ | Windows: OS 標準 / Mac: Xcode CLT または `brew install python3` |
|
|
41
|
+
| 常駐の仕組み | Windows(WSL): `/etc/wsl.conf` に `[boot] systemd=true` / Mac: launchd(標準で入っています) |
|
|
42
|
+
| sudo | Windows のみ(node のリンク作成③と linger 設定⑥)。★**Mac では使いません** |
|
|
43
|
+
| Claude Code | `npm i -g @anthropic-ai/claude-code` + `claude` でログイン(★先に済ませてください) |
|
|
44
|
+
|
|
45
|
+
> 開発・テストには Node 22 以上が要ります(`npm test` が `--experimental-strip-types` を使うため)。顧客が実行するのはコンパイル済みの JS なので 18 で足ります。
|
|
22
46
|
|
|
23
47
|
### setup.sh がやること
|
|
24
48
|
|
|
@@ -29,11 +53,15 @@ bash setup.sh <プロジェクト名> [プロジェクトディレクトリ]
|
|
|
29
53
|
② conductor 本体インストール (npm i -g @tyhld/conductor)
|
|
30
54
|
③ node symlink (/usr/local/bin/node → 実体) ← sudo
|
|
31
55
|
④ .conductor.env 作成 (対話入力) ← URL/SECRET入力
|
|
32
|
-
⑤
|
|
33
|
-
⑥ systemd enable (conductor@<名前>)
|
|
56
|
+
⑤ 番人一式の配置 (3フックを ~/.tyhld/hooks/ へ + フックは ~/.claude/settings.json に1本)
|
|
57
|
+
⑥ systemd enable (現場ごとの conductor@<名前> + このPCに1本の耳 conductor-ear) ← sudo (linger)
|
|
34
58
|
⑦ 職人起動の案内表示
|
|
35
59
|
```
|
|
36
60
|
|
|
61
|
+
> **耳(conductor-ear)**:指示が積まれた合図を受け取り、その現場の常駐へ「今すぐ確認して」と
|
|
62
|
+
> 伝える役です(PCに1本)。指示が届くまでの最大15秒の待ちが消えます。
|
|
63
|
+
> 入らない機でもセットアップは止まらず、常駐は15秒ごとの確認で従来どおり動きます。
|
|
64
|
+
|
|
37
65
|
顧客の手数: **1コマンド + 対話2問 + sudo + Claude Code 起動 = 実質3アクション**
|
|
38
66
|
|
|
39
67
|
### インストール(認証不要)
|
|
@@ -57,8 +85,104 @@ npm i -g @tyhld/conductor
|
|
|
57
85
|
|
|
58
86
|
| ファイル | 役割 | 設置先 |
|
|
59
87
|
|----------|------|--------|
|
|
60
|
-
|
|
|
61
|
-
| `settings.json` |
|
|
88
|
+
| 番人一式(`cc2_guard.py` ほか) | コマンド実行・ファイル書き込み・外部接続の前後で自動検査する安全装置。**段2(2026-07-23)から3フック立て**:`cc2_guard.py`(実行前チェック)/`permission_request_hook.py`(お客さまへの確認カード)/`stop_hook.py`(完了通知)と、その依存(`policy.py`/`summary_ja.py`/`hook_datalink.py`)。**実体は `scripts/hooks/`(正本1本)** | **1台に1箇所** `~/.tyhld/hooks/` に一式を設置。番人フックは**顧客PCの `~/.claude/settings.json` に1本だけ**登録し、この1台の全現場で共有します(各現場の `.claude/settings.json` には番人フックを書きません=二重番人を作らない) |
|
|
89
|
+
| `settings.json` | 各現場の権限ルール(`deny` と現場固有の `sandbox`)。**許可/確認の判定は番人が持つため `allow`・`ask` は空**(既定 allow、危険と関門だけ番人が止める) | プロジェクトの `.claude/settings.json` として配置 |
|
|
90
|
+
|
|
91
|
+
> **番人(`cc2_guard.py`)はこのフォルダには置いていません。**
|
|
92
|
+
> 以前はここに複製を置いていましたが、正本が更新されても複製が古いまま取り残され、
|
|
93
|
+
> 守りの内容がずれてしまいました。現在は**正本1本だけ**を配布しています
|
|
94
|
+
> (`setup.sh` が自動で配置するので、お客さまの作業は変わりません)。
|
|
95
|
+
|
|
96
|
+
### 🌐 弊社のサービスへの接続は聞かれません
|
|
97
|
+
|
|
98
|
+
職人が弊社のサービス(管制画面・保管庫・認証など)へつなぐとき、これまでは**そのたびに
|
|
99
|
+
「外部へ接続してよいか」と確認**が出ていました。弊社の設備は既定で許可済みにしたので、
|
|
100
|
+
**もう聞かれません**。
|
|
101
|
+
|
|
102
|
+
- 許可したのは**弊社の設備だけ**です(`ty-hld.com` と弊社の認証サービス)。
|
|
103
|
+
- **お客さまがお使いの外部サービス**(データベースや公開先など)は**含めていません**。
|
|
104
|
+
そちらは今までどおり、必要なときに確認が出ます。
|
|
105
|
+
- すでにお使いの環境には、下の更新コマンドで届きます。
|
|
106
|
+
|
|
107
|
+
### 📮 「終わりました」の報告や、読むだけの調べものは聞かれません
|
|
108
|
+
|
|
109
|
+
職人が管制へ**完了を伝える**とき、**データベースの構造を読むだけ**のときなどは、人の判断が要らない安全な動作なので**確認を出さずに自動で通します**(これまでは確認が担当のパソコン側に出て、職人が止まってしまうことがありました)。
|
|
110
|
+
|
|
111
|
+
- **一件ずつ許可するのではなく「職人の安全な報告・読み取り」というまとまり(カテゴリ)で許可**しています。同じ種類の道具が増えても、同じまとまりで拾えるので、そのつど設定を足す必要がありません。
|
|
112
|
+
- 自動で通すのは**報告(完了通知・返信・開発ログ)**と**読み取り専用の調べもの**だけです。**書き込み・作成・変更**(データベースへの書込や設定の反映など)は**含めていません**——今までどおり止まります。
|
|
113
|
+
- 鍵や秘密が写り得るものも**含めていません**。
|
|
114
|
+
- すでにお使いの環境には、下の更新コマンドで届きます。
|
|
115
|
+
|
|
116
|
+
### 🔄 すでにお使いの環境を最新にするには
|
|
117
|
+
|
|
118
|
+
安全装置を新しくしたときは、**次の1コマンド**でこの1台のすべての現場が最新になります。
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# まず「何が変わるか」を確認(書き込みません)
|
|
122
|
+
bash scripts/update-guard.sh
|
|
123
|
+
|
|
124
|
+
# 内容に納得したら適用
|
|
125
|
+
APPLY=1 bash scripts/update-guard.sh
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- **お客さまが加えた設定は消えません。** 変更するのは「安全装置の呼び出し先」と「禁止ルールの追加」だけで、
|
|
129
|
+
許可した作業や独自の設定はそのまま残ります。
|
|
130
|
+
- **書き換える前に自動でバックアップ**を取ります(`settings.json.bak.日付`)。
|
|
131
|
+
- **何度実行しても同じ結果**です。すでに最新なら「最新です」と表示して何もしません。
|
|
132
|
+
- 適用後は職人(Claude Code)を再起動してください。
|
|
133
|
+
- **★このコマンドを1回流すと、以後は安全装置が自動で最新になります。**
|
|
134
|
+
1時間ごとに「安全装置の中身と呼び出し先」だけを揃える見張りが入ります(許可のルールや
|
|
135
|
+
お掃除には触れません)。直したのに古いまま動き続ける、を防ぐためです(ADR-012)。
|
|
136
|
+
安全装置を作りかけの状態では配りません(作業中の壊れたものが全現場へ広がらないように)。
|
|
137
|
+
|
|
138
|
+
### 🔒 秘密のファイルと安全装置は自動で守られます
|
|
139
|
+
|
|
140
|
+
お客さまの環境では、次のファイルへの**書き込みが自動でお断り**されます。職人(AI)が
|
|
141
|
+
うっかり、あるいは指示を取り違えて書き換えてしまう事故を防ぐためです。
|
|
142
|
+
|
|
143
|
+
| 守るもの | 例 | なぜ |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| パスワードや鍵の設定ファイル | `.env` / `.env.local` | 接続情報が壊れる・漏れるのを防ぐ |
|
|
146
|
+
| **安全装置そのもの** | `.claude/settings.json` / `.claude/hooks/` / `~/.tyhld/` | ここを書き換えられると**守り自体を外せてしまう**ため |
|
|
147
|
+
| 鍵ファイル | `.ssh/` の中身 / `*.pem` / `*.key` | なりすましや不正アクセスを防ぐ |
|
|
148
|
+
|
|
149
|
+
**この動作は 2026-07-20 の更新から有効です。**それ以前に設置した環境では、`.env` などへの
|
|
150
|
+
書き込みが確認なしで通っていました。もし「これまで動いていた作業が止まった」場合は、
|
|
151
|
+
上記のいずれかに書き込もうとしている可能性があります。**必要な作業であれば、人がご自身の手で
|
|
152
|
+
編集してください**(安全装置は人の操作までは止めません)。
|
|
153
|
+
|
|
154
|
+
### 🧹 貼り付けの一時ファイルは毎日自動で古い分を消します
|
|
155
|
+
|
|
156
|
+
画面に貼り付けた文章の一時ファイル(`~/.claude/paste-cache`)は、毎日自動で古い分をお掃除します(既定では1日より前の分。当日分は残ります)。
|
|
157
|
+
|
|
158
|
+
### 🖥️ 画面の見た目チェックを使うPCでは、最初に1回だけ
|
|
159
|
+
|
|
160
|
+
職人(AI)が**できあがった画面を実際に開いて確かめる**作業(ブラウザ検品)を行うPCでは、
|
|
161
|
+
その道具を**最初に1回だけ**入れてください。入れていないと、職人は確かめようとするたびに
|
|
162
|
+
**「インターネットからブラウザを取り寄せてよいか」の確認**を出して手を止めます。
|
|
163
|
+
この確認は管制の画面には出せず、**そのPCの黒い画面(ターミナル)でしか押せません**。
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# ★どちらも conductor リポジトリの中で実行します(update-guard.sh と同じ場所)。
|
|
167
|
+
|
|
168
|
+
# A) 道具を入れる(このPCに1回だけ。約150MBのダウンロードがあります)
|
|
169
|
+
bash scripts/pw-browser-install.sh # まず「何をするか」を表示(何も入れません)
|
|
170
|
+
APPLY=1 bash scripts/pw-browser-install.sh # 内容に納得したら実行
|
|
171
|
+
|
|
172
|
+
# B) 保険:取り寄せ先を「聞かずに通してよい先」として各現場へ登録する
|
|
173
|
+
bash scripts/update-allowed-domains.sh # まず「何が変わるか」を確認(書き込みません)
|
|
174
|
+
APPLY=1 bash scripts/update-allowed-domains.sh # 内容に納得したら適用
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
- **道具は1台に1箇所だけ**置きます(`~/.tyhld/pw-venv` と `~/.tyhld/pw-browsers`)。
|
|
178
|
+
現場(プロジェクト)が増えても道具は増えません。
|
|
179
|
+
- **入れた直後に、本当に動くところまで確認**して表示します(実物があるか/職人の作業場所から
|
|
180
|
+
見えるか/実際に起動するか)。動かなければその場で止めてお知らせします。
|
|
181
|
+
- **職人は自分で入れません。** 道具が無ければ職人はそこで止まり、「人の手番です」と報告します。
|
|
182
|
+
- **B は他の設定を変えません。** 「聞かずに通してよい取り寄せ先」を足すだけで、禁止ルールや
|
|
183
|
+
安全装置には一切触れません。`scripts/update-guard.sh`(全部入り)を流す場合は同じ内容が
|
|
184
|
+
一緒に届くので、B は不要です。
|
|
185
|
+
- **何度実行しても同じ結果**です(すでに入っていれば何もしません)。
|
|
62
186
|
|
|
63
187
|
### 動作の仕組み(3層防御)
|
|
64
188
|
|
|
@@ -105,13 +229,19 @@ cp settings.json .claude/settings.json
|
|
|
105
229
|
|
|
106
230
|
```bash
|
|
107
231
|
# hooks ディレクトリを作成
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
232
|
+
# ★番人は「1台に1箇所」だけ置きます(現場ごとのコピーは作りません)
|
|
233
|
+
mkdir -p ~/.tyhld/hooks
|
|
234
|
+
|
|
235
|
+
# 番人(正本)と、その設定ファイルをコピー
|
|
236
|
+
# ★safe_verbs.json は番人と「同じフォルダ」に置く必要があります(番人が自分の隣を読むため)。
|
|
237
|
+
cp ../scripts/hooks/cc2_guard.py ~/.tyhld/hooks/cc2_guard.py
|
|
238
|
+
cp ../scripts/hooks/safe_verbs.json ~/.tyhld/hooks/safe_verbs.json 2>/dev/null || true
|
|
239
|
+
chmod +x ~/.tyhld/hooks/cc2_guard.py
|
|
113
240
|
```
|
|
114
241
|
|
|
242
|
+
> 通常は `setup.sh` が上記を自動で行います。手作業でのコピーは、
|
|
243
|
+
> 既存環境を手直しする場合のみお使いください。
|
|
244
|
+
|
|
115
245
|
Claude Code の設定(`.claude/settings.json` または `~/.claude/settings.json`)に以下を追加:
|
|
116
246
|
|
|
117
247
|
```json
|
|
@@ -123,7 +253,7 @@ Claude Code の設定(`.claude/settings.json` または `~/.claude/settings.js
|
|
|
123
253
|
"hooks": [
|
|
124
254
|
{
|
|
125
255
|
"type": "command",
|
|
126
|
-
"command": "python3
|
|
256
|
+
"command": "python3 /home/<ユーザー名>/.tyhld/hooks/cc2_guard.py"
|
|
127
257
|
}
|
|
128
258
|
]
|
|
129
259
|
}
|
|
@@ -183,15 +313,45 @@ Claude Code の設定(`.claude/settings.json` または `~/.claude/settings.js
|
|
|
183
313
|
### cc2_guard.py の安全動詞を追加する
|
|
184
314
|
|
|
185
315
|
プロジェクト固有のCLIツール(例: `turbo`, `nx`)を自動許可したい場合、
|
|
186
|
-
`cc2_guard.py`
|
|
316
|
+
**ソースは編集せず**、同梱の `safe_verbs.json`(雛形)を `cc2_guard.py` と同じディレクトリに置いて `add` に足す:
|
|
187
317
|
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
# ... 既存の動詞 ...
|
|
191
|
-
'turbo', 'nx', # 追加
|
|
192
|
-
}
|
|
318
|
+
```json
|
|
319
|
+
{ "add": ["turbo", "nx"] }
|
|
193
320
|
```
|
|
194
321
|
|
|
322
|
+
一時的なら環境変数でも可: `CC2_SAFE_VERBS="turbo nx"`。設定が無ければ既定値で動く(fail-safe)。
|
|
323
|
+
|
|
324
|
+
> ⚠️ **allow を広げるのは信頼の委譲です。** `rm` / `sudo` / `bash` / `eval` / `psql` 等の任意実行・権限昇格・破壊系は
|
|
325
|
+
> 封じてあり(`NEVER_CONFIGURABLE`)、書いても白になりません。`settings.json` の `deny` も常に優先されます。
|
|
326
|
+
> 詳細な手順と危険性は `scripts/hooks/README.md` を参照。
|
|
327
|
+
|
|
328
|
+
## 撤去(アンインストール)
|
|
329
|
+
|
|
330
|
+
采配くんの systemd 常駐を止めて、インストールしたユニットを削除します。
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
# まず何が消えるか確認する(何も変更しません)
|
|
334
|
+
bash uninstall.sh --dry-run
|
|
335
|
+
|
|
336
|
+
# 実行(確認プロンプトあり)
|
|
337
|
+
bash uninstall.sh
|
|
338
|
+
|
|
339
|
+
# 確認を省く場合
|
|
340
|
+
bash uninstall.sh --yes
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**仕組み**: インストーラは「自分が入れたユニット」を台帳 `~/.config/conductor/installed-units.txt` に記録します。`uninstall.sh` はその台帳を読んで消すため、ユニット名の形に依存せず、あなたが自作した無関係な `conductor-*.service` を巻き込みません。
|
|
344
|
+
|
|
345
|
+
台帳が無い古い環境では、`conductor@*.service` と `conductor-*.service` を走査するフォールバックが働きます。この場合は自作ユニットを巻き込む可能性があるため、**必ず `--dry-run` で対象を確認してから**実行してください。
|
|
346
|
+
|
|
347
|
+
**`uninstall.sh` が消さないもの**(必要なら手動で実施してください):
|
|
348
|
+
|
|
349
|
+
| 対象 | 消さない理由 | 手動で消す場合 |
|
|
350
|
+
|------|-------------|---------------|
|
|
351
|
+
| linger 設定 | 他のサービスでも使われ得るため | `loginctl disable-linger "$USER"` |
|
|
352
|
+
| `~/.conductor.env` | 接続情報(秘密)を含むため | `rm ~/.conductor.env` |
|
|
353
|
+
| npm パッケージ | パッケージ管理は利用者の領分 | `npm uninstall -g @tyhld/conductor` |
|
|
354
|
+
|
|
195
355
|
## 注意事項
|
|
196
356
|
|
|
197
357
|
- `settings.json` の deny は cc2_guard.py の allow より**優先**されます(多重防御)。Hook が「安全」と判断しても settings の deny に引っかかればブロックされます。
|