codex-to-im 1.0.42 → 1.0.44

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.
@@ -1,44 +1,28 @@
1
- # Token Validation Commands
2
-
3
- After writing config.env, validate each enabled platform's credentials to catch typos and configuration errors early.
4
-
5
- ## Telegram
6
-
7
- ```bash
8
- curl -s "https://api.telegram.org/bot${TOKEN}/getMe"
9
- ```
10
- Expected: response contains `"ok":true`. If not, the Bot Token is invalid — re-check with @BotFather.
11
-
12
- ## Discord
13
-
14
- Verify token format matches: `[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+`
15
-
16
- A format mismatch means the token was copied incorrectly from the Discord Developer Portal.
17
-
18
- ## Feishu / Lark
19
-
20
- ```bash
21
- curl -s -X POST "${DOMAIN}/open-apis/auth/v3/tenant_access_token/internal" \
22
- -H "Content-Type: application/json" \
23
- -d '{"app_id":"...","app_secret":"..."}'
24
- ```
25
- Expected: response contains `"code":0`. If not, check that App ID and App Secret match the Feishu Developer Console.
26
-
27
- ## QQ
28
-
29
- Step 1 — Get access token:
30
- ```bash
31
- curl -s -X POST "https://bots.qq.com/app/getAppAccessToken" \
32
- -H "Content-Type: application/json" \
33
- -d '{"appId":"...","clientSecret":"..."}'
34
- ```
35
- Expected: response contains `access_token`.
36
-
37
- Step 2 — Verify gateway connectivity:
38
- ```bash
39
- curl -s "https://api.sgroup.qq.com/gateway" \
40
- -H "Authorization: QQBot <access_token>"
41
- ```
42
- Expected: response contains a gateway URL.
43
-
44
- If either step fails, verify the App ID and App Secret from https://q.qq.com.
1
+ # Token Validation Commands
2
+
3
+ After writing `config.env`, validate each enabled platform's credentials to catch typos and configuration errors early.
4
+
5
+ ## Feishu / Lark
6
+
7
+ ```bash
8
+ curl -s -X POST "${DOMAIN}/open-apis/auth/v3/tenant_access_token/internal" \
9
+ -H "Content-Type: application/json" \
10
+ -d '{"app_id":"...","app_secret":"..."}'
11
+ ```
12
+
13
+ Expected: response contains `"code":0`. If not, check that App ID, App Secret, and site/domain match the Feishu Developer Console.
14
+
15
+ ## Weixin
16
+
17
+ Weixin uses the local linked-account store instead of a bot token. Validate it with:
18
+
19
+ ```bash
20
+ codex-to-im status
21
+ ```
22
+
23
+ If no linked account is available, run:
24
+
25
+ ```bash
26
+ cd /path/to/codex-to-im
27
+ npm run weixin:login
28
+ ```
@@ -8,7 +8,7 @@
8
8
 
9
9
  1. Run the local doctor script or inspect the workbench logs to identify the issue
10
10
  2. Check that Node.js >= 20 is installed: `node --version`
11
- 3. Check that Claude Code CLI is available: `claude --version`
11
+ 3. Check that Codex CLI is available: `codex --version`
12
12
  4. Verify config exists: `ls -la ~/.codex-to-im/config.env`
13
13
  5. Check logs for startup errors in `~/.codex-to-im/logs/`
14
14
 
@@ -23,12 +23,11 @@
23
23
 
24
24
  **Steps**:
25
25
 
26
- 1. Verify the bot token is valid with the local workbench test tools or doctor script
27
- 2. Check allowed user IDs in config -- if set, only listed users can interact
28
- 3. For Telegram: ensure you've sent `/start` to the bot first
29
- 4. For Discord: verify the bot has been invited to the server with message read permissions
30
- 5. For Feishu: confirm the app has been approved and event subscriptions are configured
31
- 6. Check recent logs for incoming message events under `~/.codex-to-im/logs/`
26
+ 1. Verify the channel credentials with the local workbench test tools or doctor script
27
+ 2. Check allowed user IDs in config -- if set, only listed users can interact
28
+ 3. For Feishu: confirm the app has been approved and event subscriptions are configured
29
+ 4. For Weixin: confirm the linked account is logged in and assigned to the channel instance
30
+ 5. Check recent logs for incoming message events under `~/.codex-to-im/logs/`
32
31
 
33
32
  ## Feishu streaming cards not working
34
33
 
@@ -52,12 +51,12 @@
52
51
 
53
52
  ## Permission timeout
54
53
 
55
- **Symptoms**: Claude Code session starts but times out waiting for tool approval.
54
+ **Symptoms**: Codex session starts but times out waiting for tool approval.
56
55
 
57
56
  **Steps**:
58
57
 
59
- 1. The bridge runs Claude Code in non-interactive mode; ensure your Claude Code configuration allows the necessary tools
60
- 2. Consider using `--allowedTools` in your configuration to pre-approve common tools
58
+ 1. The bridge runs Codex through the SDK/CLI; ensure the configured sandbox and approval policy match the requested task
59
+ 2. Use the IM permission prompt to approve the requested tool, or adjust Codex approval settings if you expect unattended execution
61
60
  3. Check network connectivity if the timeout occurs during API calls
62
61
 
63
62
  ## High memory usage
@@ -71,7 +70,7 @@
71
70
  ```
72
71
  Stop the bridge, then start it again from the local workbench
73
72
  ```
74
- 3. If the issue persists, check how many concurrent sessions are active -- each Claude Code session consumes memory
73
+ 3. If the issue persists, check how many concurrent sessions are active -- each Codex session consumes memory
75
74
  4. Review logs for error loops that may cause memory leaks
76
75
 
77
76
  ## Stale PID file
package/scripts/build.js CHANGED
@@ -6,17 +6,12 @@ const common = {
6
6
  format: 'esm',
7
7
  target: 'node20',
8
8
  external: [
9
- // SDK must stay external — it spawns a CLI subprocess and resolves
10
- // dist/cli.js relative to its own package location. Bundling it
11
- // breaks that path resolution.
12
- '@anthropic-ai/claude-agent-sdk',
13
9
  '@openai/codex-sdk',
14
10
  // Keep large IM SDKs external so global/local npm installs resolve them
15
11
  // from node_modules instead of inflating daemon.mjs.
16
12
  '@larksuiteoapi/node-sdk',
17
- 'discord.js',
18
- // discord.js optional native deps
19
- 'bufferutil', 'utf-8-validate', 'zlib-sync', 'erlpack',
13
+ // ws optional native deps
14
+ 'bufferutil', 'utf-8-validate',
20
15
  // Node.js built-ins
21
16
  'fs', 'path', 'os', 'crypto', 'http', 'https', 'net', 'tls',
22
17
  'stream', 'events', 'url', 'util', 'child_process', 'worker_threads',
package/scripts/daemon.sh CHANGED
@@ -38,34 +38,16 @@ ensure_built() {
38
38
  }
39
39
 
40
40
  # Clean environment for subprocess isolation.
41
- clean_env() {
42
- unset CLAUDECODE 2>/dev/null || true
43
-
44
- local runtime
45
- runtime=$(grep "^CTI_RUNTIME=" "$CTI_HOME/config.env" 2>/dev/null | head -1 | cut -d= -f2- | tr -d "'" | tr -d '"' || true)
46
- runtime="${runtime:-claude}"
47
-
48
- local mode="${CTI_ENV_ISOLATION:-inherit}"
49
- if [ "$mode" = "strict" ]; then
50
- case "$runtime" in
51
- codex)
52
- while IFS='=' read -r name _; do
53
- case "$name" in ANTHROPIC_*) unset "$name" 2>/dev/null || true ;; esac
54
- done < <(env)
55
- ;;
56
- claude)
57
- # Keep ANTHROPIC_* (from config.env) — needed for third-party API providers.
58
- # Strip OPENAI_* to avoid cross-runtime leakage.
59
- while IFS='=' read -r name _; do
60
- case "$name" in OPENAI_*) unset "$name" 2>/dev/null || true ;; esac
61
- done < <(env)
62
- ;;
63
- auto)
64
- # Keep both ANTHROPIC_* and OPENAI_* for auto mode
65
- ;;
66
- esac
67
- fi
68
- }
41
+ clean_env() {
42
+ unset CLAUDECODE 2>/dev/null || true
43
+
44
+ local mode="${CTI_ENV_ISOLATION:-inherit}"
45
+ if [ "$mode" = "strict" ]; then
46
+ while IFS='=' read -r name _; do
47
+ case "$name" in ANTHROPIC_*) unset "$name" 2>/dev/null || true ;; esac
48
+ done < <(env)
49
+ fi
50
+ }
69
51
 
70
52
  read_pid() {
71
53
  [ -f "$PID_FILE" ] && cat "$PID_FILE" 2>/dev/null || echo ""
@@ -133,8 +115,7 @@ case "${1:-help}" in
133
115
  exit 1
134
116
  fi
135
117
 
136
- # Source config.env BEFORE clean_env so that CTI_ANTHROPIC_PASSTHROUGH
137
- # and other CTI_* flags are available when clean_env checks them.
118
+ # Source config.env BEFORE clean_env so CTI_* flags are available.
138
119
  [ -f "$CTI_HOME/config.env" ] && set -a && source "$CTI_HOME/config.env" && set +a
139
120
 
140
121
  clean_env
package/scripts/doctor.sh CHANGED
@@ -37,210 +37,26 @@ get_config() { grep "^$1=" "$CONFIG_FILE" 2>/dev/null | head -1 | cut -d= -f2- |
37
37
 
38
38
  # --- Read runtime setting ---
39
39
  SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
40
- CTI_RUNTIME=$(get_config CTI_RUNTIME)
41
- CTI_RUNTIME="${CTI_RUNTIME:-claude}"
42
- echo "Runtime: $CTI_RUNTIME"
43
- echo ""
44
-
45
- # --- Claude CLI available (claude/auto modes) ---
46
- if [ "$CTI_RUNTIME" = "claude" ] || [ "$CTI_RUNTIME" = "auto" ]; then
47
- # Resolve CLI path matching the daemon's checkCliCompatibility logic:
48
- # - Version >= 2.x AND all required flags present
49
- # - Skip candidates that fail either check (same as resolveClaudeCliPath)
50
- CLAUDE_PATH=""
51
- CLAUDE_VER=""
52
- CLAUDE_COMPAT=1
53
- REQUIRED_FLAGS="output-format input-format permission-mode setting-sources"
54
-
55
- # Helper: check if a candidate passes both version and flags checks.
56
- # Sets CLAUDE_PATH/CLAUDE_VER/CLAUDE_COMPAT on success.
57
- try_candidate() {
58
- local cand="$1"
59
- [ -x "$cand" ] || return 1
60
- local ver
61
- ver=$("$cand" --version 2>/dev/null || true)
62
- [ -z "$ver" ] && return 1
63
- local major
64
- major=$(echo "$ver" | sed -E -n 's/^[^0-9]*([0-9]+)\..*/\1/p' | head -1)
65
- if [ -z "$major" ] || ! [ "$major" -ge 2 ] 2>/dev/null; then
66
- echo " (skipping $cand — version $ver is too old, need >= 2.x)"
67
- return 1
68
- fi
69
- # Version OK — check flags
70
- local help_text
71
- help_text=$("$cand" --help 2>&1 || true)
72
- for flag in $REQUIRED_FLAGS; do
73
- if ! echo "$help_text" | grep -q "$flag"; then
74
- echo " (skipping $cand — version $ver OK but missing --$flag)"
75
- return 1
76
- fi
77
- done
78
- # Fully compatible
79
- CLAUDE_PATH="$cand"
80
- CLAUDE_VER="$ver"
81
- CLAUDE_COMPAT=0
82
- return 0
83
- }
84
-
85
- # 1. Explicit env var — if set, daemon uses it unconditionally (no fallback).
86
- # Doctor must mirror this: report on this path only, never scan further.
87
- CTI_EXE=$(get_config CTI_CLAUDE_CODE_EXECUTABLE 2>/dev/null || true)
88
- if [ -n "$CTI_EXE" ]; then
89
- if [ -x "$CTI_EXE" ]; then
90
- if ! try_candidate "$CTI_EXE"; then
91
- # Explicit path is set but incompatible — daemon WILL use it and fail.
92
- # Report it as the selected CLI so the user sees the real problem.
93
- CLAUDE_PATH="$CTI_EXE"
94
- CLAUDE_VER=$("$CTI_EXE" --version 2>/dev/null || echo "unknown")
95
- # CLAUDE_COMPAT stays 1 (incompatible) — checks below will report failure
96
- fi
97
- else
98
- CLAUDE_PATH="$CTI_EXE"
99
- CLAUDE_VER="(not executable)"
100
- fi
101
- fi
102
-
103
- # 2. All PATH candidates (only if no explicit env var was set)
104
- if [ -z "$CTI_EXE" ] && [ -z "$CLAUDE_PATH" ]; then
105
- ALL_CLAUDES=$(which -a claude 2>/dev/null || true)
106
- for cand in $ALL_CLAUDES; do
107
- try_candidate "$cand" && break
108
- done
109
- fi
110
-
111
- # 3. Well-known locations (only if no explicit env var was set)
112
- if [ -z "$CTI_EXE" ] && [ -z "$CLAUDE_PATH" ]; then
113
- for cand in \
114
- "$HOME/.claude/local/claude" \
115
- "$HOME/.local/bin/claude" \
116
- "/usr/local/bin/claude" \
117
- "/opt/homebrew/bin/claude" \
118
- "$HOME/.npm-global/bin/claude"; do
119
- try_candidate "$cand" && break
120
- done
121
- fi
122
-
123
- if [ -n "$CLAUDE_PATH" ] && [ "$CLAUDE_COMPAT" = "0" ]; then
124
- check "Claude CLI compatible (${CLAUDE_VER} at ${CLAUDE_PATH})" 0
125
- elif [ -n "$CLAUDE_PATH" ]; then
126
- # Path found but incompatible (too old, missing flags, or not executable)
127
- check "Claude CLI compatible (${CLAUDE_VER} at ${CLAUDE_PATH} — incompatible, see above)" 1
128
- else
129
- if [ "$CTI_RUNTIME" = "claude" ]; then
130
- check "Claude CLI available (not found in PATH or common locations)" 1
131
- else
132
- check "Claude CLI available (not found — will use Codex fallback)" 0
133
- fi
134
- fi
135
-
136
- # --- Claude CLI authenticated ---
137
- # Skip this check if third-party API credentials are configured in config.env.
138
- # In that mode the bridge authenticates via ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN,
139
- # not via `claude auth login`, so a missing interactive login is expected and harmless.
140
- HAS_THIRD_PARTY_AUTH=false
141
- if [ -f "$CONFIG_FILE" ] && grep -qE "^ANTHROPIC_(API_KEY|AUTH_TOKEN)=" "$CONFIG_FILE" 2>/dev/null; then
142
- HAS_THIRD_PARTY_AUTH=true
143
- fi
144
- if [ -n "$CLAUDE_PATH" ] && [ "$CLAUDE_COMPAT" = "0" ]; then
145
- if [ "$HAS_THIRD_PARTY_AUTH" = "true" ]; then
146
- check "Claude CLI auth (skipped — using third-party API credentials from config.env)" 0
147
- else
148
- AUTH_OUT=$("$CLAUDE_PATH" auth status 2>&1 || true)
149
- if echo "$AUTH_OUT" | grep -qiE 'loggedIn.*true|logged.in'; then
150
- check "Claude CLI authenticated" 0
151
- else
152
- check "Claude CLI authenticated (run 'claude auth login')" 1
153
- fi
154
- fi
155
- fi
156
-
157
- # --- ANTHROPIC_* env reachability ---
158
- # Check whether ANTHROPIC_* vars are configured in config.env.
159
- # This is what matters for the daemon — the current shell env is irrelevant
160
- # because on macOS the daemon runs under launchd with only plist env vars.
161
- HAS_ANTHROPIC_CONFIG=false
162
- if [ -f "$CONFIG_FILE" ]; then
163
- if grep -q "^ANTHROPIC_" "$CONFIG_FILE" 2>/dev/null; then
164
- HAS_ANTHROPIC_CONFIG=true
165
- fi
166
- fi
167
- if [ "$HAS_ANTHROPIC_CONFIG" = "true" ]; then
168
- check "ANTHROPIC_* vars in config.env (third-party API provider)" 0
169
-
170
- PLIST_FILE="$HOME/Library/LaunchAgents/com.codex-to-im.bridge.plist"
171
-
172
- # On macOS, verify the launchd plist also has the vars
173
- if [ "$(uname -s)" = "Darwin" ] && [ -f "$PLIST_FILE" ]; then
174
- if grep -q "ANTHROPIC_" "$PLIST_FILE" 2>/dev/null; then
175
- check "ANTHROPIC_* vars in launchd plist" 0
176
- else
177
- check "ANTHROPIC_* vars in launchd plist (NOT present — restart bridge to regenerate plist)" 1
178
- fi
179
- fi
180
-
181
- # If bridge is running, verify the LIVE process has the vars.
182
- # The plist may be correct on disk but if the daemon hasn't been
183
- # restarted since the plist was regenerated, it still runs with the
184
- # old environment.
185
- BRIDGE_PID=$(cat "$PID_FILE" 2>/dev/null || true)
186
- if [ -n "$BRIDGE_PID" ] && kill -0 "$BRIDGE_PID" 2>/dev/null; then
187
- # ps eww shows the process environment on macOS/Linux
188
- PROC_ENV=$(ps eww -p "$BRIDGE_PID" 2>/dev/null || true)
189
- if echo "$PROC_ENV" | grep -q "ANTHROPIC_"; then
190
- check "Running bridge process has ANTHROPIC_* env vars" 0
191
- else
192
- check "Running bridge process has ANTHROPIC_* env vars (NOT in process env — restart the bridge)" 1
193
- fi
194
- fi
195
- else
196
- check "ANTHROPIC_* vars in config.env (not set — OK if using default Anthropic auth)" 0
197
- fi
198
-
199
- # --- SDK cli.js resolvable ---
200
- SDK_CLI=""
201
- for candidate in \
202
- "$SKILL_DIR/node_modules/@anthropic-ai/claude-agent-sdk/cli.js" \
203
- "$SKILL_DIR/node_modules/@anthropic-ai/claude-agent-sdk/dist/cli.js"; do
204
- if [ -f "$candidate" ]; then
205
- SDK_CLI="$candidate"
206
- break
207
- fi
208
- done
209
- if [ -n "$SDK_CLI" ]; then
210
- check "Claude SDK cli.js exists ($SDK_CLI)" 0
211
- else
212
- if [ "$CTI_RUNTIME" = "claude" ]; then
213
- check "Claude SDK cli.js exists (not found — run 'npm install' in $SKILL_DIR)" 1
214
- else
215
- check "Claude SDK cli.js exists (not found — OK for auto/codex mode)" 0
216
- fi
217
- fi
218
- fi
219
-
220
- # --- Codex checks (codex/auto modes) ---
221
- if [ "$CTI_RUNTIME" = "codex" ] || [ "$CTI_RUNTIME" = "auto" ]; then
222
- if command -v codex &>/dev/null; then
223
- CODEX_VER=$(codex --version 2>/dev/null || echo "unknown")
224
- check "Codex CLI available (${CODEX_VER})" 0
225
- else
226
- if [ "$CTI_RUNTIME" = "codex" ]; then
227
- check "Codex CLI available (not found in PATH)" 1
228
- else
229
- check "Codex CLI available (not found — will use Claude)" 0
230
- fi
231
- fi
232
-
233
- # Check @openai/codex-sdk
234
- CODEX_SDK="$SKILL_DIR/node_modules/@openai/codex-sdk"
235
- if [ -d "$CODEX_SDK" ]; then
236
- check "@openai/codex-sdk installed" 0
237
- else
238
- if [ "$CTI_RUNTIME" = "codex" ]; then
239
- check "@openai/codex-sdk installed (not found — run 'npm install' in $SKILL_DIR)" 1
240
- else
241
- check "@openai/codex-sdk installed (not found — OK for auto/claude mode)" 0
242
- fi
243
- fi
40
+ CTI_RUNTIME=$(get_config CTI_RUNTIME)
41
+ CTI_RUNTIME="codex"
42
+ echo "Runtime: $CTI_RUNTIME"
43
+ echo ""
44
+
45
+ # --- Codex checks ---
46
+ if command -v codex &>/dev/null; then
47
+ CODEX_VER=$(codex --version 2>/dev/null || echo "unknown")
48
+ check "Codex CLI available (${CODEX_VER})" 0
49
+ else
50
+ check "Codex CLI available (not found in PATH)" 1
51
+ fi
52
+
53
+ # Check @openai/codex-sdk
54
+ CODEX_SDK="$SKILL_DIR/node_modules/@openai/codex-sdk"
55
+ if [ -d "$CODEX_SDK" ]; then
56
+ check "@openai/codex-sdk installed" 0
57
+ else
58
+ check "@openai/codex-sdk installed (not found — run 'npm install' in $SKILL_DIR)" 1
59
+ fi
244
60
 
245
61
  # Check Codex auth: any of CTI_CODEX_API_KEY / CODEX_API_KEY / OPENAI_API_KEY,
246
62
  # or `codex auth status` showing logged-in (interactive login).
@@ -253,18 +69,13 @@ if [ "$CTI_RUNTIME" = "codex" ] || [ "$CTI_RUNTIME" = "auto" ]; then
253
69
  CODEX_AUTH=0
254
70
  fi
255
71
  fi
256
- if [ "$CODEX_AUTH" = "0" ]; then
257
- check "Codex auth available (API key or login)" 0
258
- else
259
- if [ "$CTI_RUNTIME" = "codex" ]; then
260
- check "Codex auth available (set OPENAI_API_KEY or run 'codex auth login')" 1
261
- else
262
- check "Codex auth available (not found — needed only for Codex fallback)" 0
263
- fi
264
- fi
265
- fi
266
-
267
- # --- dist/daemon.mjs freshness ---
72
+ if [ "$CODEX_AUTH" = "0" ]; then
73
+ check "Codex auth available (API key or login)" 0
74
+ else
75
+ check "Codex auth available (set OPENAI_API_KEY or run 'codex auth login')" 1
76
+ fi
77
+
78
+ # --- dist/daemon.mjs freshness ---
268
79
  DAEMON_MJS="$SKILL_DIR/dist/daemon.mjs"
269
80
  if [ -f "$DAEMON_MJS" ]; then
270
81
  STALE_SRC=$(find "$SKILL_DIR/src" -name '*.ts' -newer "$DAEMON_MJS" 2>/dev/null | head -1)
@@ -295,25 +106,10 @@ if [ -f "$CONFIG_FILE" ]; then
295
106
  fi
296
107
 
297
108
  # --- Load config for channel checks ---
298
- if [ -f "$CONFIG_FILE" ]; then
299
- CTI_CHANNELS=$(get_config CTI_ENABLED_CHANNELS)
300
-
301
- # --- Telegram ---
302
- if echo "$CTI_CHANNELS" | grep -q telegram; then
303
- TG_TOKEN=$(get_config CTI_TG_BOT_TOKEN)
304
- if [ -n "$TG_TOKEN" ]; then
305
- TG_RESULT=$(curl -s --max-time 5 "https://api.telegram.org/bot${TG_TOKEN}/getMe" 2>/dev/null || echo '{"ok":false}')
306
- if echo "$TG_RESULT" | grep -q '"ok":true'; then
307
- check "Telegram bot token is valid" 0
308
- else
309
- check "Telegram bot token is valid (getMe failed)" 1
310
- fi
311
- else
312
- check "Telegram bot token configured" 1
313
- fi
314
- fi
315
-
316
- # --- Feishu ---
109
+ if [ -f "$CONFIG_FILE" ]; then
110
+ CTI_CHANNELS=$(get_config CTI_ENABLED_CHANNELS)
111
+
112
+ # --- Feishu ---
317
113
  if echo "$CTI_CHANNELS" | grep -q feishu; then
318
114
  FS_APP_ID=$(get_config CTI_FEISHU_APP_ID)
319
115
  FS_SECRET=$(get_config CTI_FEISHU_APP_SECRET)
@@ -337,51 +133,10 @@ if [ -f "$CONFIG_FILE" ]; then
337
133
  fi
338
134
  else
339
135
  check "Feishu app credentials configured" 1
340
- fi
341
- fi
342
-
343
- # --- QQ ---
344
- if echo "$CTI_CHANNELS" | grep -q qq; then
345
- QQ_APP_ID=$(get_config CTI_QQ_APP_ID)
346
- QQ_APP_SECRET=$(get_config CTI_QQ_APP_SECRET)
347
- if [ -n "$QQ_APP_ID" ] && [ -n "$QQ_APP_SECRET" ]; then
348
- QQ_TOKEN_RESULT=$(curl -s --max-time 10 -X POST "https://bots.qq.com/app/getAppAccessToken" \
349
- -H "Content-Type: application/json" \
350
- -d "{\"appId\":\"${QQ_APP_ID}\",\"clientSecret\":\"${QQ_APP_SECRET}\"}" 2>/dev/null || echo '{}')
351
- QQ_ACCESS_TOKEN=$(echo "$QQ_TOKEN_RESULT" | sed -n 's/.*"access_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
352
- if [ -n "$QQ_ACCESS_TOKEN" ]; then
353
- check "QQ app credentials are valid (access_token obtained)" 0
354
- # Verify gateway availability
355
- QQ_GW_RESULT=$(curl -s --max-time 10 "https://api.sgroup.qq.com/gateway" \
356
- -H "Authorization: QQBot ${QQ_ACCESS_TOKEN}" 2>/dev/null || echo '{}')
357
- if echo "$QQ_GW_RESULT" | grep -q '"url"'; then
358
- check "QQ gateway is reachable" 0
359
- else
360
- check "QQ gateway is reachable (GET /gateway failed)" 1
361
- fi
362
- else
363
- check "QQ app credentials are valid (getAppAccessToken failed)" 1
364
- fi
365
- else
366
- check "QQ app credentials configured" 1
367
- fi
368
- fi
369
-
370
- # --- Discord ---
371
- if echo "$CTI_CHANNELS" | grep -q discord; then
372
- DC_TOKEN=$(get_config CTI_DISCORD_BOT_TOKEN)
373
- if [ -n "$DC_TOKEN" ]; then
374
- if echo "${DC_TOKEN}" | grep -qE '^[A-Za-z0-9_-]{20,}\.'; then
375
- check "Discord bot token format" 0
376
- else
377
- check "Discord bot token format (does not match expected pattern)" 1
378
- fi
379
- else
380
- check "Discord bot token configured" 1
381
- fi
382
- fi
383
-
384
- # --- Weixin ---
136
+ fi
137
+ fi
138
+
139
+ # --- Weixin ---
385
140
  if echo "$CTI_CHANNELS" | grep -q weixin; then
386
141
  WX_ACCOUNTS_FILE="$CTI_HOME/data/weixin-accounts.json"
387
142
  if [ -f "$WX_ACCOUNTS_FILE" ]; then
@@ -28,34 +28,15 @@ build_env_dict() {
28
28
  ;; esac
29
29
  done < <(env)
30
30
 
31
- # Forward runtime-specific API keys
32
- local runtime
33
- runtime=$(grep "^CTI_RUNTIME=" "$CTI_HOME/config.env" 2>/dev/null | head -1 | cut -d= -f2- | tr -d "'" | tr -d '"' || true)
34
- runtime="${runtime:-claude}"
35
-
36
- case "$runtime" in
37
- codex|auto)
38
- for var in OPENAI_API_KEY CODEX_API_KEY CTI_CODEX_API_KEY CTI_CODEX_BASE_URL; do
39
- local val="${!var:-}"
40
- [ -z "$val" ] && continue
41
- dict+="${indent}<key>${var}</key>\n${indent}<string>${val}</string>\n"
42
- done
43
- ;;
44
- esac
45
- case "$runtime" in
46
- claude|auto)
47
- # Auto-forward all ANTHROPIC_* env vars (sourced from config.env by daemon.sh).
48
- # Third-party API providers need these to reach the CLI subprocess.
49
- while IFS='=' read -r name val; do
50
- case "$name" in ANTHROPIC_*)
51
- dict+="${indent}<key>${name}</key>\n${indent}<string>${val}</string>\n"
52
- ;; esac
53
- done < <(env)
54
- ;;
55
- esac
56
-
57
- echo -e "$dict"
58
- }
31
+ # Forward Codex/OpenAI credentials used by the Codex runtime.
32
+ for var in OPENAI_API_KEY CODEX_API_KEY CTI_CODEX_API_KEY CTI_CODEX_BASE_URL; do
33
+ local val="${!var:-}"
34
+ [ -z "$val" ] && continue
35
+ dict+="${indent}<key>${var}</key>\n${indent}<string>${val}</string>\n"
36
+ done
37
+
38
+ echo -e "$dict"
39
+ }
59
40
 
60
41
  generate_plist() {
61
42
  local node_path