@tyhld/conductor 0.11.8 → 0.11.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tyhld/conductor",
3
- "version": "0.11.8",
3
+ "version": "0.11.9",
4
4
  "description": "采配くん管制の見守りアプリ(conductor-agent)。各PCで常駐し、中央(devlog-tracker)へ定期的に生存報告(heartbeat)を送る常駐CLI。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -44,3 +44,24 @@ approval_policy = "on-request"
44
44
  # 作業フォルダの中だけ書けるようにします。
45
45
  # ★danger-full-access は絶対に使わないでください(守りが丸ごと外れます)。
46
46
  sandbox_mode = "workspace-write"
47
+
48
+ # ── 通信を開ける(★えふさん決定 2026-09-06) ──────────────────────────────
49
+ # 【なぜ要るか=実測 2026-09-06・本番現場 meilink】
50
+ # これが無いと、箱の中から外へ一切出られません。実機で curl が終了コード6(名前解決失敗)
51
+ # になり、npm install / npx vercel / gh / 公開サイトの確認 が全部できませんでした。
52
+ # =Codex の職人は「ローカルの編集とコミット」しかできない状態でした。
53
+ # ★開けるのは【通信だけ】です。箱(作業フォルダの外へは書けない)はそのまま残ります。
54
+ # danger-full-access は使いません(あれは箱ごと外す設定)。
55
+ [sandbox_workspace_write]
56
+ network_access = true
57
+
58
+ # ── 管制の MCP(Claude と同じ接続先・同じ道具名) ─────────────────────────
59
+ # 便の末尾に付く「完了時は conductor_complete を呼んでください」を Codex でも通すための登録です。
60
+ # ・道具の名前は Claude と同じ conductor_complete(管制側の実物で確認済み)。
61
+ # ・★合言葉はここに書きません(平文を置かない)。この受け口は Supabase の JWT を求めるので、
62
+ # 人が1回だけ `codex mcp login ai-kana` でログインして持たせます(=人の手番)。
63
+ # ・長生きする JWT を用意する運用にするなら、環境変数の【名前だけ】を書く形にもできます:
64
+ # codex mcp add ai-kana --url <URL> --bearer-token-env-var <環境変数名>
65
+ # (値は config.toml に載りません)
66
+ [mcp_servers.ai-kana]
67
+ url = "https://ai-kana.ty-hld.com/api/mcp/mcp"
@@ -16,7 +16,10 @@
16
16
  # 書き換えて ~/.codex/hooks.json へ登録する
17
17
  # 3. 顧客が足した他のイベント・他のキーは触らない(見本のイベントだけ差し替える)
18
18
  # 4. 完了の受け口(codex-notify.py)を固定パス $GUARD_HOME へ置く
19
- # 5. ~/.codex/config.toml の【3つの鍵だけ】を揃える(notify / approval_policy / sandbox_mode)
19
+ # 5. ~/.codex/config.toml の【決まった場所だけ】を揃える
20
+ # ・3つの鍵: notify / approval_policy / sandbox_mode
21
+ # ・[sandbox_workspace_write] network_access = true … 箱はそのままに通信だけ開ける
22
+ # ・[mcp_servers.ai-kana] url = … … 管制のMCP(★合言葉は書かない)
20
23
  # ★他の行(信頼の記録・現場ごとの設定・顧客が足した設定)は1文字も触らない
21
24
  #
22
25
  # ★Claude 側(~/.claude/settings.json)には一切触れません。
@@ -161,13 +164,18 @@ fi
161
164
  # 現場ごとの信頼設定が入っている。まるごと上書きすると信頼が消え、番人が動かなくなる
162
165
  # (=いちばん危ない壊し方)。だから既存の行は一切動かさず、必要な3つだけを揃える。
163
166
  CODEX_CONFIG="${CODEX_CONFIG:-$HOME/.codex/config.toml}"
164
- info "設定の確認: $CODEX_CONFIGnotify / approval_policy / sandbox_mode の3つだけ)"
165
- APPLY="$APPLY" NOTIFY_CMD="$CODEX_NOTIFY_DST" python3 - "$CODEX_CONFIG" <<'PYEOF'
167
+ # 管制の MCPClaude と同じ接続先・同じ道具名 conductor_complete)。★合言葉は書かない。
168
+ CODEX_MCP_URL="${CODEX_MCP_URL:-https://ai-kana.ty-hld.com/api/mcp/mcp}"
169
+ info "設定の確認: $CODEX_CONFIG(notify / approval_policy / sandbox_mode + 通信の許可 + 管制のMCP)"
170
+ APPLY="$APPLY" NOTIFY_CMD="$CODEX_NOTIFY_DST" MCP_URL="$CODEX_MCP_URL" \
171
+ python3 - "$CODEX_CONFIG" <<'PYEOF'
166
172
  import os, re, shutil, sys, time
167
173
 
168
174
  path = sys.argv[1]
169
175
  apply_mode = os.environ.get("APPLY") == "1"
170
176
  notify_cmd = os.environ["NOTIFY_CMD"]
177
+ mcp_url = os.environ["MCP_URL"]
178
+ MCP_URL = mcp_url
171
179
 
172
180
  # ★この3つだけを揃える(値の意味は scripts/codex/config.sample.toml に書いてある)。
173
181
  # approval_policy: on-request … 承認の場面を【番人(PermissionRequest)に通す】ための設定。
@@ -203,14 +211,61 @@ for line in head:
203
211
  continue # 既存の行は落として、下で揃えた値を書き直す
204
212
  kept.append(line)
205
213
 
214
+ # ── ★表([section])で管理するもの ────────────────────────────────────────────
215
+ # ここも「その表の中身だけ」を揃える。他の表(信頼の記録・現場ごとの設定)は1文字も触らない。
216
+ #
217
+ # [sandbox_workspace_write] network_access = true
218
+ # ★箱(作業フォルダの外へ書けない)はそのままに、通信だけを開ける(えふさん決定 2026-09-06)。
219
+ # 実測 2026-09-06: これが無いと箱の中から外へ出られず、curl は終了コード6(名前解決失敗)。
220
+ # meilink の日常作業(npm install / npx vercel / gh / 公開サイトの確認)が全部できなかった。
221
+ # ★danger-full-access は使わない(それは箱ごと外す設定)。
222
+ #
223
+ # [mcp_servers.ai-kana] url = …
224
+ # ★Claude と同じ接続先。道具の名前も同じ(conductor_complete)なので、便の末尾の
225
+ # 「完了時は conductor_complete を呼ぶ」がそのまま通る。
226
+ # ★合言葉はここに書かない(平文を置かない)。この受け口は Supabase の JWT を要求するので、
227
+ # 人が1回 `codex mcp login ai-kana` でログインして持たせる(=人の手番)。
228
+ # 長生きする JWT を用意する運用にするなら、codex mcp add の --bearer-token-env-var で
229
+ # 環境変数の【名前だけ】を書く形にもできる(値は config.toml に載らない)。
230
+ TABLES = {
231
+ "sandbox_workspace_write": ["network_access = true"],
232
+ "mcp_servers.ai-kana": [f'url = "{MCP_URL}"'],
233
+ }
234
+
235
+
236
+ def table_slice(all_lines, name):
237
+ """表 [name] の範囲(見出し行の位置, 次の表の直前)を返す。無ければ (None, None)。"""
238
+ header = re.compile(r"^\s*\[" + re.escape(name) + r"\]\s*$")
239
+ start = next((i for i, ln in enumerate(all_lines) if header.match(ln)), None)
240
+ if start is None:
241
+ return None, None
242
+ end = len(all_lines)
243
+ for j in range(start + 1, len(all_lines)):
244
+ if all_lines[j].lstrip().startswith("["):
245
+ end = j
246
+ break
247
+ return start, end
248
+
249
+
250
+ def table_needs_update(all_lines, name, wants):
251
+ start, end = table_slice(all_lines, name)
252
+ if start is None:
253
+ return True
254
+ body = [ln.strip() for ln in all_lines[start + 1:end] if ln.strip()]
255
+ return any(w not in body for w in wants)
256
+
257
+
258
+ tables_missing = [t for t, w in TABLES.items() if table_needs_update(tail, t, w)]
206
259
  missing = [k for k in WANT if current.get(k) != WANT[k]]
207
- if not missing:
260
+ if not missing and not tables_missing:
208
261
  print("[codex-hooks] 最新です(変更なし)")
209
262
  sys.exit(0)
210
263
  for k in missing:
211
264
  now = current.get(k)
212
265
  print(f"[codex-hooks] 要更新: {k}"
213
266
  + (f"(いま: {now})" if now else "(未設定)"))
267
+ for t in tables_missing:
268
+ print(f"[codex-hooks] 要更新: [{t}]")
214
269
  if not apply_mode:
215
270
  print("[codex-hooks] (予定)上記を揃えます(APPLY=1 で実行)")
216
271
  sys.exit(0)
@@ -219,6 +274,21 @@ banner = "# ★采配くんが管理する3行(notify / approval_policy / sand
219
274
  while kept and not kept[-1].strip():
220
275
  kept.pop()
221
276
  new_head = kept + ([""] if kept else []) + [banner] + [WANT[k] for k in ("notify", "approval_policy", "sandbox_mode")] + [""]
277
+
278
+ # 表は「在れば中身を揃える/無ければ末尾に足す」。順番は TOML では意味を持たない。
279
+ for name, wants in TABLES.items():
280
+ start, end = table_slice(tail, name)
281
+ if start is None:
282
+ while tail and not tail[-1].strip():
283
+ tail.pop()
284
+ tail += ["", f"[{name}]"] + wants
285
+ continue
286
+ body = [ln for ln in tail[start + 1:end]]
287
+ keys = [w.split("=", 1)[0].strip() for w in wants]
288
+ kept_body = [ln for ln in body
289
+ if not any(re.match(r"^\s*" + re.escape(k) + r"\s*=", ln) for k in keys)]
290
+ tail[start + 1:end] = wants + kept_body
291
+
222
292
  out = "\n".join(new_head + tail).rstrip("\n") + "\n"
223
293
 
224
294
  os.makedirs(os.path.dirname(path), exist_ok=True)
@@ -240,7 +310,7 @@ except Exception as e: # noqa: BLE001
240
310
  sys.exit(0)
241
311
 
242
312
  os.replace(tmp, path) # 置換は原子的
243
- print("[codex-hooks] 揃えました(3行)")
313
+ print("[codex-hooks] 揃えました")
244
314
  PYEOF
245
315
 
246
316
  if [[ "$APPLY" == "1" ]]; then