From 1934da0441863b96efe46395011df17c0f58f284 Mon Sep 17 00:00:00 2001 From: boxp Date: Mon, 16 Mar 2026 05:22:21 +0000 Subject: [PATCH 1/2] fix(openclaw): allow xai-x-search, xai-web-search and WebFetch in Claude settings --- argoproj/openclaw/deployment-openclaw.yaml | 24 +++++++++++++ docs/project_docs/T-20260315-001/plan.md | 40 ++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 docs/project_docs/T-20260315-001/plan.md diff --git a/argoproj/openclaw/deployment-openclaw.yaml b/argoproj/openclaw/deployment-openclaw.yaml index 6473fb922..572bac579 100644 --- a/argoproj/openclaw/deployment-openclaw.yaml +++ b/argoproj/openclaw/deployment-openclaw.yaml @@ -98,6 +98,30 @@ spec: ln -sfn "$item" "$CLAUDE_DIR/$name" done fi + # Patch settings.json to allow cron-invoked skills (xai-x-search, + # xai-web-search) and WebFetch so daily-morning-trend-research + # is not blocked by approval prompts. + node -e " + const fs = require('fs'); + const p = '/home/node/.claude/settings.json'; + try { + const s = JSON.parse(fs.readFileSync(p, 'utf8')); + const add = [ + 'Bash(bash /home/node/.claude/skills/xai-x-search/scripts/search.sh *)', + 'Bash(bash /home/node/.claude/skills/xai-web-search/scripts/search.sh *)', + 'WebFetch' + ]; + s.permissions = s.permissions || {}; + s.permissions.allow = s.permissions.allow || []; + const before = JSON.stringify(s); + add.forEach(t => { if (!s.permissions.allow.includes(t)) s.permissions.allow.push(t); }); + if (JSON.stringify(s) !== before) { + fs.writeFileSync(p, JSON.stringify(s, null, 2) + '\n'); + } + } catch (e) { + console.error('settings.json patch skipped:', e.message); + } + " exec node dist/index.js gateway --bind lan --port 18789 ports: - containerPort: 18789 diff --git a/docs/project_docs/T-20260315-001/plan.md b/docs/project_docs/T-20260315-001/plan.md new file mode 100644 index 000000000..541dcd5cf --- /dev/null +++ b/docs/project_docs/T-20260315-001/plan.md @@ -0,0 +1,40 @@ +# T-20260315-001: Daily Morning Research 用承認設定修正 + +## 背景 + +`daily-morning-trend-research` cron ジョブが 2026-03-11 以降 6 日間連続で承認ブロックされ、 +完全に機能停止していた。 + +## 原因 + +`/home/node/.claude/settings.json` の `permissions.allow` に以下が不足: + +1. `Bash(bash *)` パターン — `bash /home/node/.claude/skills/xai-x-search/scripts/search.sh` が承認要求される +2. `WebFetch` — はてなブックマーク等の外部 URL 取得が承認要求される + +cron 実行時はインタラクティブな承認応答ができないため、全データソースへのアクセスがブロックされていた。 + +## 修正内容 + +`deployment-openclaw.yaml` のコンテナ起動スクリプトに、`settings.json` へのパッチ処理を追加: + +- `Bash(bash /home/node/.claude/skills/xai-x-search/scripts/search.sh *)` +- `Bash(bash /home/node/.claude/skills/xai-web-search/scripts/search.sh *)` +- `WebFetch` + +これにより、Pod 起動時に自動的に必要な許可設定が追加される。 + +## 設計判断 + +- **プロジェクトレベル `.claude/settings.json` ではなく deployment パッチを選択した理由**: + Claude Code の `.claude` ディレクトリはセキュリティ上の制約があり、 + また Docker イメージ内に bake された settings.json を runtime でパッチする方が + lolice リポジトリの責務範囲内で完結する +- **最小権限の原則**: `Bash(bash *)` のような広範なパターンではなく、 + 特定のスクリプトパスのみを許可 +- **冪等性**: `includes()` チェックにより重複追加を防止 + +## リスク + +- スキルスクリプトのパスが変更された場合、パッチ対象のパスも更新が必要 +- Node.js の `require('fs')` に依存(コンテナイメージに Node.js が存在する前提) From 4aeb72e4d9691557ab143cd7636ab41feb8c7ce0 Mon Sep 17 00:00:00 2001 From: boxp Date: Tue, 17 Mar 2026 01:45:07 +0000 Subject: [PATCH 2/2] refactor(openclaw): replace runtime settings.json patch with ConfigMap - Co-Authored-By: Claude Opus 4.6 --- .../openclaw/configmap-claude-settings.yaml | 62 +++++++++++++++++++ argoproj/openclaw/deployment-openclaw.yaml | 33 +++------- argoproj/openclaw/kustomization.yaml | 1 + docs/project_docs/T-20260315-001/plan.md | 46 +++++++++----- 4 files changed, 103 insertions(+), 39 deletions(-) create mode 100644 argoproj/openclaw/configmap-claude-settings.yaml diff --git a/argoproj/openclaw/configmap-claude-settings.yaml b/argoproj/openclaw/configmap-claude-settings.yaml new file mode 100644 index 000000000..6253641ff --- /dev/null +++ b/argoproj/openclaw/configmap-claude-settings.yaml @@ -0,0 +1,62 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: claude-settings + namespace: openclaw +data: + settings.json: | + { + "permissions": { + "defaultMode": "acceptEdits", + "allow": [ + "Read", + "Glob", + "Grep", + "Bash(git *)", + "Bash(gh *)", + "Bash(ghq *)", + "Bash(gwq *)", + "Bash(mkdir *)", + "Bash(touch *)", + "Bash(cp *)", + "Bash(mv *)", + "Bash(rm *)", + "Bash(ls *)", + "Bash(cat *)", + "Bash(head *)", + "Bash(tail *)", + "Bash(find *)", + "Bash(grep *)", + "Bash(sed *)", + "Bash(awk *)", + "Bash(sort *)", + "Bash(uniq *)", + "Bash(wc *)", + "Bash(diff *)", + "Bash(chmod *)", + "Bash(ln *)", + "Bash(curl *)", + "Bash(tar *)", + "Bash(xargs *)", + "Bash(dirname *)", + "Bash(basename *)", + "Bash(tee *)", + "Bash(codex *)", + "Bash(docker *)", + "Bash(bb *)", + "Bash(man *)", + "Bash(apt-get *)", + "Bash(apt *)", + "Bash(bash /home/node/.claude/skills/xai-x-search/scripts/search.sh *)", + "Bash(bash /home/node/.claude/skills/xai-web-search/scripts/search.sh *)", + "WebFetch", + "mcp__grafana__*" + ] + }, + "mcpServers": { + "grafana": { + "command": "mcp-grafana", + "args": ["--transport", "stdio", "--disable-write"] + } + } + } diff --git a/argoproj/openclaw/deployment-openclaw.yaml b/argoproj/openclaw/deployment-openclaw.yaml index 572bac579..001fa4dc8 100644 --- a/argoproj/openclaw/deployment-openclaw.yaml +++ b/argoproj/openclaw/deployment-openclaw.yaml @@ -88,7 +88,7 @@ spec: DOTFILES_DIR=/home/node/ghq/github.com/boxp/dotfiles # Symlink individual dotfiles into .claude instead of replacing # the entire directory, to preserve Dockerfile's config.json - # and settings.json (trustedWorkspaces, allowedCommands). + # and ConfigMap-mounted settings.json. CLAUDE_DIR=/home/node/.claude if [ -d "$DOTFILES_DIR/.claude" ]; then for item in "$DOTFILES_DIR/.claude"/* "$DOTFILES_DIR/.claude"/.*; do @@ -98,30 +98,6 @@ spec: ln -sfn "$item" "$CLAUDE_DIR/$name" done fi - # Patch settings.json to allow cron-invoked skills (xai-x-search, - # xai-web-search) and WebFetch so daily-morning-trend-research - # is not blocked by approval prompts. - node -e " - const fs = require('fs'); - const p = '/home/node/.claude/settings.json'; - try { - const s = JSON.parse(fs.readFileSync(p, 'utf8')); - const add = [ - 'Bash(bash /home/node/.claude/skills/xai-x-search/scripts/search.sh *)', - 'Bash(bash /home/node/.claude/skills/xai-web-search/scripts/search.sh *)', - 'WebFetch' - ]; - s.permissions = s.permissions || {}; - s.permissions.allow = s.permissions.allow || []; - const before = JSON.stringify(s); - add.forEach(t => { if (!s.permissions.allow.includes(t)) s.permissions.allow.push(t); }); - if (JSON.stringify(s) !== before) { - fs.writeFileSync(p, JSON.stringify(s, null, 2) + '\n'); - } - } catch (e) { - console.error('settings.json patch skipped:', e.message); - } - " exec node dist/index.js gateway --bind lan --port 18789 ports: - containerPort: 18789 @@ -235,6 +211,10 @@ spec: - name: data mountPath: /home/node/.codex subPath: codex + - name: claude-settings + mountPath: /home/node/.claude/settings.json + subPath: settings.json + readOnly: true # Config manager sidecar: periodically syncs ConfigMap to PVC # so that OpenClaw's chokidar file watcher detects changes and # hot-reloads configuration without Pod restart. @@ -419,6 +399,9 @@ spec: - name: openclaw-config configMap: name: openclaw-config + - name: claude-settings + configMap: + name: claude-settings - name: gh-wrapper configMap: name: gh-wrapper diff --git a/argoproj/openclaw/kustomization.yaml b/argoproj/openclaw/kustomization.yaml index 3e296d43f..c47d5a8af 100644 --- a/argoproj/openclaw/kustomization.yaml +++ b/argoproj/openclaw/kustomization.yaml @@ -7,6 +7,7 @@ resources: - external-secret.yaml - external-secret-litellm.yaml - configmap-openclaw.yaml + - configmap-claude-settings.yaml - configmap-gh-wrapper.yaml - configmap-litellm.yaml - deployment-openclaw.yaml diff --git a/docs/project_docs/T-20260315-001/plan.md b/docs/project_docs/T-20260315-001/plan.md index 541dcd5cf..bde199ae7 100644 --- a/docs/project_docs/T-20260315-001/plan.md +++ b/docs/project_docs/T-20260315-001/plan.md @@ -9,32 +9,50 @@ `/home/node/.claude/settings.json` の `permissions.allow` に以下が不足: -1. `Bash(bash *)` パターン — `bash /home/node/.claude/skills/xai-x-search/scripts/search.sh` が承認要求される -2. `WebFetch` — はてなブックマーク等の外部 URL 取得が承認要求される +1. `Bash(bash /home/node/.claude/skills/xai-x-search/scripts/search.sh *)` — X検索スクリプト実行 +2. `Bash(bash /home/node/.claude/skills/xai-web-search/scripts/search.sh *)` — Web検索スクリプト実行 +3. `WebFetch` — はてなブックマーク等の外部 URL 取得 cron 実行時はインタラクティブな承認応答ができないため、全データソースへのアクセスがブロックされていた。 -## 修正内容 +## 修正内容(v2: ConfigMap-only) -`deployment-openclaw.yaml` のコンテナ起動スクリプトに、`settings.json` へのパッチ処理を追加: +### 前案(v1: deployment 起動時パッチ)の問題点 +- `deployment-openclaw.yaml` の起動スクリプトに `node -e` でインラインJavaScriptを埋め込み、 + `settings.json` を動的にパッチしていた +- deployment の責務が過剰(起動スクリプトが設定管理を担うべきではない) +- `settings.json` の内容が Docker イメージ内とパッチスクリプトの2箇所に分散 +- パッチの前後で settings.json の内容が不透明(Git diff で最終状態が見えない) + +### v2: ConfigMap-only アプローチ + +`settings.json` 全体を ConfigMap (`configmap-claude-settings.yaml`) として定義し、 +Deployment の volumeMount で `/home/node/.claude/settings.json` に直接マウントする。 + +**変更ファイル:** +- `configmap-claude-settings.yaml` (新規): settings.json の全内容を静的に定義 +- `deployment-openclaw.yaml`: `node -e` パッチを削除、ConfigMap の volumeMount を追加 +- `kustomization.yaml`: 新しい ConfigMap リソースを追加 + +**追加する権限:** - `Bash(bash /home/node/.claude/skills/xai-x-search/scripts/search.sh *)` - `Bash(bash /home/node/.claude/skills/xai-web-search/scripts/search.sh *)` - `WebFetch` -これにより、Pod 起動時に自動的に必要な許可設定が追加される。 - ## 設計判断 -- **プロジェクトレベル `.claude/settings.json` ではなく deployment パッチを選択した理由**: - Claude Code の `.claude` ディレクトリはセキュリティ上の制約があり、 - また Docker イメージ内に bake された settings.json を runtime でパッチする方が - lolice リポジトリの責務範囲内で完結する +- **ConfigMap-only で成立する理由**: `settings.json` は Docker イメージに bake されていたが、 + 内容は静的な設定であり、ConfigMap として外部化する方が GitOps の原則に合致する。 + ConfigMap の volumeMount (subPath) で直接ファイルをマウントするため、 + 起動スクリプトでの動的パッチは不要。 +- **dotfiles symlink の `settings.json` 除外は維持**: ConfigMap マウントされたファイルが + dotfiles によって上書きされることを防ぐ安全策として、既存の case 除外をそのまま残す。 - **最小権限の原則**: `Bash(bash *)` のような広範なパターンではなく、 - 特定のスクリプトパスのみを許可 -- **冪等性**: `includes()` チェックにより重複追加を防止 + 特定のスクリプトパスのみを許可。 ## リスク -- スキルスクリプトのパスが変更された場合、パッチ対象のパスも更新が必要 -- Node.js の `require('fs')` に依存(コンテナイメージに Node.js が存在する前提) +- スキルスクリプトのパスが変更された場合、ConfigMap の更新が必要 +- Docker イメージ内の settings.json と ConfigMap の内容が乖離する可能性があるが、 + volumeMount が優先されるため実害はない