Files
telegram-bot/src/doctor.js
T
JianMiauandClaude Fable 5 5b32816ad4 建立 telegram-codex-bot:串接 Codex CLI 的 Telegram bot(npm 包)
摘要:
用 Telegram 操控本機 Codex CLI 的 bot,附 web 控制台,封裝為 npm 包。

內容:
- 零依賴(僅 Node 內建模組);bot 以 codex exec --json 驅動,支援會話延續
  (exec resume)、workspace-write 沙盒、圖片輸入、進度回報與 ctx%/tokens footer
- web 控制台(pm2 託管,預設 127.0.0.1:3799):Bot 管理分頁(新增/編輯/啟停,
  token 自動驗證、工作目錄用原生視窗選、模型/推理強度/速度下拉,清單來自
  ~/.codex/models_cache.json)+ PM2 檢視分頁(狀態/port/log/啟停)
- CLI:start(環境檢查後把控制台掛上 pm2)/ stop / restart / delete / status /
  logs / web / doctor
- Windows 相容:解析 codex.cmd shim 直接以 node 執行、taskkill 整樹砍程序、
  資料夾選擇視窗以 TopMost 透明 owner 置中

影響:
新專案初始版本;bot 設定存於 ~/.tgcodex/bots/<name>/,含明文 token 的實例
設定不進版控(.gitignore 已涵蓋 tgcodex.config.json 與 .tgcodex/)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 16:25:39 +08:00

78 lines
2.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
// 環境檢查:start 前確認 codex CLI、pm2、git 等工具是否就緒。
const { execSync } = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
function tryExec(cmd) {
try {
return execSync(cmd, { encoding: 'utf-8', windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'], timeout: 15000 }).trim();
} catch {
return null;
}
}
// 回傳 [{ name, ok, required, message }]
function runChecks() {
const checks = [];
const nodeMajor = Number(process.versions.node.split('.')[0]);
checks.push({
name: 'Node.js',
ok: nodeMajor >= 18,
required: true,
message: nodeMajor >= 18 ? process.version : `${process.version}(需要 ≥ 18`,
});
const codexVer = tryExec('codex --version');
checks.push({
name: 'Codex CLI',
ok: !!codexVer,
required: true,
message: codexVer || '找不到 codex,請安裝:npm install -g @openai/codex',
});
if (codexVer) {
const authFile = path.join(os.homedir(), '.codex', 'auth.json');
const loggedIn = fs.existsSync(authFile);
checks.push({
name: 'Codex 登入',
ok: loggedIn,
required: false,
message: loggedIn ? '已登入' : '尚未登入,請先執行:codex loginbot 啟動後才需要)',
});
}
const pm2Ver = tryExec('pm2 -v');
checks.push({
name: 'pm2',
ok: !!pm2Ver,
required: true,
message: pm2Ver ? `v${pm2Ver.split('\n').pop()}` : '找不到 pm2,請安裝:npm install -g pm2',
});
const gitVer = tryExec('git --version');
checks.push({
name: 'git',
ok: !!gitVer,
required: false,
message: gitVer || '找不到 git(非必要:bot 以 --skip-git-repo-check 執行,但 Codex 要做版控操作時會需要)',
});
return checks;
}
function printChecks(checks) {
for (const c of checks) {
const icon = c.ok ? '✅' : c.required ? '❌' : '⚠️';
console.log(`${icon} ${c.name}${c.message}`);
}
}
function hasBlocker(checks) {
return checks.some((c) => c.required && !c.ok);
}
module.exports = { runChecks, printChecks, hasBlocker };