#!/bin/bash set -e # 1. 创建持久化挂载目录 mkdir -p /data/.claude /data/.cloudcli /data/app /data/.taskmaster /data/.codex_data/sessions /data/.codex_data/state # 2. 软链接常规目录 if [ ! -L "$HOME/.claude" ]; then rm -rf "$HOME/.claude" ln -s /data/.claude "$HOME/.claude" fi if [ ! -L "$HOME/.claude.json" ]; then rm -f "$HOME/.claude.json" ln -s /data/.claude.json "$HOME/.claude.json" fi if [ ! -L "$HOME/.cloudcli" ]; then rm -rf "$HOME/.cloudcli" ln -s /data/.cloudcli "$HOME/.cloudcli" fi if [ ! -L "$HOME/.taskmaster" ]; then rm -rf "$HOME/.taskmaster" ln -s /data/.taskmaster "$HOME/.taskmaster" fi if [ ! -L "$HOME/app" ]; then ln -s /data/app "$HOME/app" fi # ==================== 针对 .codex 的安全持久化 ==================== # 清理可能存在的旧软链接 [ -L "$HOME/.codex" ] && rm -f "$HOME/.codex" mkdir -p "$HOME/.codex" # 1. 软链接 sessions(文本 jsonl 在网络卷完全安全) [ -d "$HOME/.codex/sessions" ] && [ ! -L "$HOME/.codex/sessions" ] && rm -rf "$HOME/.codex/sessions" ln -sf /data/.codex_data/sessions "$HOME/.codex/sessions" # 2. 恢复 SQLite 元数据与索引(从网络卷复制到容器本地磁盘,避免网络锁损坏) if [ -d "/data/.codex_data/state" ]; then cp -u /data/.codex_data/state/*.sqlite* "$HOME/.codex/" 2>/dev/null || true cp -u /data/.codex_data/state/*.jsonl "$HOME/.codex/" 2>/dev/null || true fi # 3. 后台定时同步线程:定期将本地生成的 SQLite 和 session_index 备份回 /data ( while true; do sleep 60 if [ -d "$HOME/.codex" ]; then mkdir -p /data/.codex_data/state # 同步 sqlite 与 index 文件 cp -u "$HOME"/.codex/*.sqlite* /data/.codex_data/state/ 2>/dev/null || true cp -u "$HOME"/.codex/*.jsonl /data/.codex_data/state/ 2>/dev/null || true fi done ) & # =================================================================== # 3. 自动生成 Claude Code 配置 mkdir -p "$HOME/.claude" cat < "$HOME/.claude/settings.json" { "cleanupPeriodDays": 720, "env": { "API_TIMEOUT_MS": "1800000", "BASH_DEFAULT_TIMEOUT_MS": "1800000", "BASH_MAX_TIMEOUT_MS": "7200000", "ANTHROPIC_BASE_URL": "${ANTHROPIC_BASE_URL:-}", "ANTHROPIC_AUTH_TOKEN": "${ANTHROPIC_AUTH_TOKEN:-}", "CLAUDE_CODE_ATTRIBUTION_HEADER": "0", "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1", "DISABLE_INSTALLATION_CHECKS": "1", "CLAUDE_CODE_PROXY_RESOLVES_HOSTS": "1", "CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS": "1", "ENABLE_TOOL_SEARCH": "true", "DISABLE_AUTOUPDATER": "1", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-sonnet-x[1m]", "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-sonnet-x[1m]", "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-x[1m]", "ANTHROPIC_MODEL": "claude-sonnet-x[1m]", "ANTHROPIC_REASONING_MODEL": "claude-sonnet-x[1m]" }, "permissions": { "allow": [ "Read", "Write", "Edit", "Bash(*)", "Glob", "Grep", "Agent", "WebFetch", "WebSearch", "NotebookEdit", "mcp__serper__google_search", "mcp__tavily__tavily_search", "mcp__tavily__tavily_research" ], "deny": [ "Read(.*env*)", "Read(~/.ssh/*)", "Bash(rm -rf / *)" ] }, "skipWebFetchPreflight": true, "effortLevel": "high", "autoUpdatesChannel": "latest" } EOF # 4. 自动生成 Codex 配置 CODEX_KEY_RESOLVED="${CODEX_API_KEY:-}" if [ -z "$CODEX_KEY_RESOLVED" ] && [ -f /data/.codex/gw_token ]; then CODEX_KEY_RESOLVED="$(head -n 1 /data/.codex/gw_token | tr -d '[:space:]')" fi if [ -n "$CODEX_KEY_RESOLVED" ]; then export OPENAI_API_KEY="$CODEX_KEY_RESOLVED" fi cat < "$HOME/.codex/config.toml" cli_auth_credentials_store = "file" model_provider = "${CODEX_PROVIDER:-custom}" model = "${CODEX_MODEL:-gpt-5.6-luna}" sandbox_mode = "danger-full-access" approval_policy = "never" [model_providers.custom] name = "Custom" base_url = "${CODEX_BASE_URL:-https://cpa.freecode.eu.cc/v1}" wire_api = "responses" env_key = "OPENAI_API_KEY" EOF cat < "$HOME/.codex/auth.json" { "OPENAI_API_KEY": "${CODEX_KEY_RESOLVED:-sk-dummy-key}" } EOF # 5. 创建 codex wrapper 兜底(task-master-ai 内置 codex 不在 PATH 中; # 集成端无环境变量启动时,wrapper 自动从 gw_token 注入凭证) if [ -w /usr/local/bin ] && [ ! -e /usr/local/bin/codex ] && [ -x /usr/local/lib/node_modules/task-master-ai/node_modules/.bin/codex ]; then cat <<'WEOF' > /usr/local/bin/codex #!/bin/bash if [ -z "$OPENAI_API_KEY" ] && [ -f /data/.codex/gw_token ]; then export OPENAI_API_KEY="$(head -n 1 /data/.codex/gw_token)" fi exec /usr/local/lib/node_modules/task-master-ai/node_modules/.bin/codex "$@" WEOF chmod +x /usr/local/bin/codex 2>/dev/null || true fi # 6. 全局读写赋权(随后收紧 gw_token 权限,维持 600 保密要求) chown -R 1000:1000 /data "$HOME" 2>/dev/null || true chmod -R 777 /data "$HOME" 2>/dev/null || true [ -f /data/.codex/gw_token ] && chmod 600 /data/.codex/gw_token 2>/dev/null || true # 7. 切换并启动 mkdir -p /home/node/app 2>/dev/null || true cd /home/node/app || cd / exec "$@"