cc-ding 0.3.1 → 0.4.1
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/README.md +16 -4
- package/dist/bin/cc-ding.js +5 -5
- package/dist/src/biz/api-key-manager.js +3 -3
- package/dist/src/biz/cc-ding-cli.js +66 -61
- package/dist/src/biz/claude-process.js +34 -33
- package/dist/src/biz/claude-sdk.js +2 -0
- package/dist/src/biz/command-route.js +1 -0
- package/dist/src/biz/commands.js +6 -6
- package/dist/src/biz/cron.js +6 -7
- package/dist/src/biz/doctor.js +2 -2
- package/dist/src/biz/image.js +8 -8
- package/dist/src/biz/lock.js +1 -1
- package/dist/src/biz/menu.js +2 -2
- package/dist/src/biz/messaging.js +5 -5
- package/dist/src/biz/notify.js +1 -1
- package/dist/src/biz/platform.js +2 -0
- package/dist/src/biz/quote.js +5 -5
- package/dist/src/biz/recorder.js +9 -9
- package/dist/src/biz/secrets.js +1 -0
- package/dist/src/biz/session.js +13 -13
- package/dist/src/biz/task.js +23 -22
- package/dist/src/biz/todo.js +6 -6
- package/dist/src/common.js +1 -1
- package/dist/src/constants.js +1 -1
- package/package.json +7 -11
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ pm2 start --name "cc-ding-{clientId}" npx -- -p cc-ding cc-ding run -ci {clientI
|
|
|
97
97
|
| 命令 | 说明 |
|
|
98
98
|
|------|------|
|
|
99
99
|
| `/ls [目录] [层数]` | 查看目录结构 |
|
|
100
|
-
| `/bash <命令>` |
|
|
100
|
+
| `/bash <命令>` | 执行任意命令(仅 owner/管理员,执行记录审计日志 `bash-audit.log`) |
|
|
101
101
|
|
|
102
102
|
#### 管理命令(仅 owner)
|
|
103
103
|
|
|
@@ -152,9 +152,15 @@ pm2 start --name "cc-ding-{clientId}" npx -- -p cc-ding cc-ding run -ci {clientI
|
|
|
152
152
|
| `apiKeyCfg` | API Key 池化:429 自动切换、每日 0 点重置 |
|
|
153
153
|
| `useLocalOcr` | 图片本地 OCR(默认 `true`),模型支持图片时可设 `false` |
|
|
154
154
|
| `linkConversationId` | 关联群 ID,多群共享同一 Claude 会话上下文 |
|
|
155
|
-
| `permissionMode` | Claude 进程权限模式(默认 `bypassPermissions
|
|
155
|
+
| `permissionMode` | Claude 进程权限模式(默认 `acceptEdits`;`bypassPermissions` 需显式配置,启动时会告警),可选: `default`、`acceptEdits`、`plan`、`auto`、`bypassPermissions`、`dontAsk` |
|
|
156
156
|
| `preBash` | 全局 `/bash` 前置命令 |
|
|
157
157
|
|
|
158
|
+
#### 安全说明
|
|
159
|
+
|
|
160
|
+
- 敏感字段(`clientSecret`、`dingToken`、`defaultDingToken`、`apiKeyCfg[].apiKey`)支持 `$ENV:VAR_NAME` 形式引用环境变量,避免明文落盘,例如 `"clientSecret": "$ENV:DING_SECRET"`
|
|
161
|
+
- `config.json` 与 `settings-ding.json` 写入时自动收紧为 `600` 权限
|
|
162
|
+
- `/bash` 仅限 owner/管理员执行,所有命令记录到 `~/.cc-ding/{clientId}/bash-audit.log`
|
|
163
|
+
|
|
158
164
|
### 数据存储
|
|
159
165
|
|
|
160
166
|
所有数据存储在 `~/.cc-ding/{clientId}/` 目录下:
|
|
@@ -263,7 +269,7 @@ pm2 start --name "cc-ding-{clientId}" npx -- -p cc-ding cc-ding run -ci {clientI
|
|
|
263
269
|
| Command | Description |
|
|
264
270
|
|---------|-------------|
|
|
265
271
|
| `/ls [dir] [depth]` | View directory structure |
|
|
266
|
-
| `/bash <cmd>` | Execute arbitrary commands (
|
|
272
|
+
| `/bash <cmd>` | Execute arbitrary commands (owner/admin only, audited in `bash-audit.log`) |
|
|
267
273
|
|
|
268
274
|
#### Admin (owner only)
|
|
269
275
|
|
|
@@ -318,9 +324,15 @@ pm2 start --name "cc-ding-{clientId}" npx -- -p cc-ding cc-ding run -ci {clientI
|
|
|
318
324
|
| `apiKeyCfg` | API Key pooling: auto-switch on 429, daily reset at midnight |
|
|
319
325
|
| `useLocalOcr` | Local image OCR (default `true`); set `false` if model supports images natively |
|
|
320
326
|
| `linkConversationId` | Link groups to share one Claude session context |
|
|
321
|
-
| `permissionMode` | Claude process permission mode (default `bypassPermissions`), options: `default`, `acceptEdits`, `plan`, `auto`, `bypassPermissions`, `dontAsk` |
|
|
327
|
+
| `permissionMode` | Claude process permission mode (default `acceptEdits`; `bypassPermissions` must be set explicitly and warns at startup), options: `default`, `acceptEdits`, `plan`, `auto`, `bypassPermissions`, `dontAsk` |
|
|
322
328
|
| `preBash` | Global pre-bash command for `/bash` |
|
|
323
329
|
|
|
330
|
+
#### Security Notes
|
|
331
|
+
|
|
332
|
+
- Sensitive fields (`clientSecret`, `dingToken`, `defaultDingToken`, `apiKeyCfg[].apiKey`) support `$ENV:VAR_NAME` references to environment variables, e.g. `"clientSecret": "$ENV:DING_SECRET"`
|
|
333
|
+
- `config.json` and `settings-ding.json` are written with `600` permissions
|
|
334
|
+
- `/bash` is restricted to owner/admin; all commands are audited in `~/.cc-ding/{clientId}/bash-audit.log`
|
|
335
|
+
|
|
324
336
|
### Data Storage
|
|
325
337
|
|
|
326
338
|
All data is stored under `~/.cc-ding/{clientId}/`:
|
package/dist/bin/cc-ding.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
2
|
+
"use strict";var r=exports&&exports.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});const a=require("../src/common"),g=require("commander"),t=require("../src/biz/session"),d=r(require("path")),m=require("../src/biz/cc-ding-cli"),I=require("../src/biz/lock"),u=require("../src/biz/doctor"),p=require("../src/biz/notify"),f=r(require("fs"));(0,a.loadEnv)(),process.removeAllListeners("warning"),process.setMaxListeners(0),process.on("uncaughtException",function(e){console.log("Caught exception: "+e),process.exit(1)});const o=new g.Command;o.addHelpText("before",`
|
|
3
3
|
cc-ding for connect ClaudeCode to DingDingRobot
|
|
4
4
|
`).addHelpText("after",`
|
|
5
5
|
Examples:
|
|
6
6
|
$ cc-ding init -ci {clientId} -cs {clientSecret} -m {mobile}
|
|
7
7
|
$ cc-ding run -ci {clientId}
|
|
8
|
-
`).version((0,
|
|
8
|
+
`).version((0,a.projUtil)().getPkgVersion()),o.command("init").description("\u521D\u59CB\u5316cc-ding\u914D\u7F6E\u6587\u4EF6, \u751F\u6210\u6700\u7B80config.json").requiredOption("-ci, --clientId <value>","clientId").requiredOption("-cs, --clientSecret <value>","clientSecret (\u9489\u9489Stream\u8FDE\u63A5\u5BC6\u94A5)").requiredOption("-m, --mobile <value>","mobile (\u81EA\u5DF1\u7684\u624B\u673A\u53F7, \u81EA\u52A8\u52A0\u5165\u767D\u540D\u5355)").option("-cn, --clientName <value>","clientName (\u673A\u5668\u4EBA\u540D\u79F0, \u53EF\u9009)").action(async e=>{const n=process.version.slice(1);parseInt(n.split(".")[0],10)<24&&(console.log(`
|
|
9
9
|
\u274C Node \u7248\u672C\u8FC7\u4F4E\uFF0C\u65E0\u6CD5\u6267\u884C init \u547D\u4EE4`),console.log(` \u5F53\u524D\u7248\u672C\uFF1A${n}`),console.log(" \u8981\u6C42\uFF1ANode >= 24"),console.log(`
|
|
10
10
|
\u{1F4A1} \u8BF7\u5347\u7EA7 Node \u7248\u672C\u540E\u91CD\u65B0\u8FD0\u884C
|
|
11
|
-
`),process.exit(1));const i=`${`${(0,
|
|
11
|
+
`),process.exit(1));const i=`${`${(0,t.getHomeDir)()}/.cc-ding/${e.clientId}`}/config.json`;f.default.existsSync(i)&&(console.log(`\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728: ${i}`),console.log("\u5982\u9700\u91CD\u65B0\u521D\u59CB\u5316, \u8BF7\u5148\u5220\u9664\u5DF2\u6709\u914D\u7F6E\u6587\u4EF6"),process.exit(1));const c={clientName:e.clientName||"cc\u52A9\u624B",owner:e.mobile,whiteUserList:[e.mobile],clientSecret:e.clientSecret,defaultDingToken:"<\u515C\u5E95\u9489\u9489\u673A\u5668\u4EBAToken-\u7528\u4E8E\u65E0dingToken\u7FA4\u7684\u6D88\u606F\u63A5\u6536>",conversations:[],includeThinking:!1,resultOnly:!0,debug:!1,taskQueueSize:10,taskHandlerCount:1,sessionMaxConcurrency:20};(0,t.initClientDir)(e.clientId,c),console.log("\u914D\u7F6E\u6587\u4EF6\u5DF2\u751F\u6210:",i),console.log(""),console.log("\u540E\u7EED\u6B65\u9AA4:"),console.log(" 1. \u7F16\u8F91 config.json \u6DFB\u52A0 conversations \u914D\u7F6E(\u7FA4\u804A\u9700\u914D\u7F6EdingToken)"),console.log(" 2. \u542F\u52A8\u673A\u5668\u4EBA: cc-ding run -ci",e.clientId),console.log(" 3. \u63A8\u8350PM2\u542F\u52A8:"),console.log(` pm2 start --name "cc-ding-${e.clientId}" npx -- -p cc-ding cc-ding run -ci ${e.clientId}`)}),o.command("run").description(`
|
|
12
12
|
- \u529F\u80FD: \u9489\u9489\u673A\u5668\u4EBA\u5BF9\u63A5\u672C\u5730Claude, \u652F\u6301\u4F1A\u8BDD\u6A21\u5F0F\u548C\u4EFB\u52A1\u961F\u5217\u6A21\u5F0F
|
|
13
13
|
- \u4F1A\u8BDD\u6570\u636E\u8DEF\u5F84: ~/.cc-ding/{clientId}/{MD5}/.sessions/{claudeSessionId}/session.{json|log}
|
|
14
14
|
- \u4EFB\u52A1\u6570\u636E\u8DEF\u5F84: ~/.cc-ding/{clientId}/{MD5}/.tasks/{\u65F6\u95F4\u6233}/task.{json|log}
|
|
@@ -33,5 +33,5 @@ Examples:
|
|
|
33
33
|
- 429\u81EA\u52A8\u5207\u6362: \u81EA\u52A8\u5207\u6362\u5230API Key\u6A21\u5F0F
|
|
34
34
|
- Key\u8F6E\u6362: API Key\u9047\u5230429\u6216\u8FDE\u7EEDTPM\u4E0D\u7A33\u5B9A\u65F6\u81EA\u52A8\u6362Key
|
|
35
35
|
- \u8DE8\u5929\u91CD\u7F6E: \u6BCF\u65E5\u81EA\u52A8\u91CD\u7F6EAPI Key\u72B6\u6001
|
|
36
|
-
`).requiredOption("-ci, --clientId <value>","clientId").action(async e=>{(0,
|
|
37
|
-
\u2705 \u6210\u529F: ${i.success}, \u274C \u5931\u8D25: ${i.fail}`),process.exit(i.fail>0?1:0)}),
|
|
36
|
+
`).requiredOption("-ci, --clientId <value>","clientId").action(async e=>{(0,t.ensureClientDir)(e.clientId);const n=d.default.join((0,t.getHomeDir)(),".cc-ding",e.clientId);(0,I.acquirePidLock)(n,e.clientId),await new m.DingClaude(e.clientId).run()}),o.command("doctor").description("\u68C0\u67E5\u6307\u5B9Aclient\u7684\u914D\u7F6E\u6587\u4EF6schema\u5408\u6CD5\u6027\u548C\u6709\u6548\u6027").requiredOption("-ci, --clientId <value>","clientId").action(async e=>{const n=d.default.join((0,t.getHomeDir)(),".cc-ding",e.clientId),s=(0,u.runDoctor)(n);(0,u.printDoctorResults)(s)}),o.command("notify").description("\u901A\u8FC7\u9489\u9489\u673A\u5668\u4EBA\u53D1\u9001\u6D88\u606F\u5230\u6307\u5B9A\u7FA4\u6216\u5355\u804A").requiredOption("-ci, --clientId <value>","clientId").requiredOption("-c, --conversations <value>","\u76EE\u6807\u4F1A\u8BDDID\uFF08\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF09").requiredOption("-m, --message <value>","\u6D88\u606F\u5185\u5BB9").option("-at, --atUserIds <value>","@ \u6307\u5B9A\u7528\u6237\uFF08\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF09").option("-mo, --mobile <value>","\u5355\u804A\u76EE\u6807\u624B\u673A\u53F7\uFF08\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF0C\u4E0E conversations \u4E00\u4E00\u5BF9\u5E94\uFF09").option("-md, --markdown","\u4F7F\u7528 Markdown \u683C\u5F0F\u53D1\u9001",!1).action(async e=>{const n=e.conversations.split(",").map(c=>c.trim()).filter(Boolean),s=e.atUserIds?e.atUserIds.split(",").map(c=>c.trim()).filter(Boolean):[],l=e.mobile?e.mobile.split(",").map(c=>c.trim()).filter(Boolean):[];console.log(`\u{1F4E4} \u53D1\u9001\u6D88\u606F\u5230 ${n.length} \u4E2A\u4F1A\u8BDD...`);const i=await(0,p.sendNotify)({clientId:e.clientId,message:e.message,conversationIds:n,atUserIds:s,mobiles:l,markdown:e.markdown});console.log(`
|
|
37
|
+
\u2705 \u6210\u529F: ${i.success}, \u274C \u5931\u8D25: ${i.fail}`),process.exit(i.fail>0?1:0)}),o.parse(process.argv);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var __importDefault=this&&this.__importDefault||function(s){return s&&s.__esModule?s:{default:s}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.saveClientConfig=saveClientConfig,exports.resetApiKeyCfg=resetApiKeyCfg,exports.scheduleApiKeyCfgDailyReset=scheduleApiKeyCfgDailyReset,exports.settingLabel=settingLabel,exports.rotateApiKey=rotateApiKey,exports.pickValidApiKey=pickValidApiKey,exports.ensureSettingsWithApiKey=ensureSettingsWithApiKey,exports.isQuotaExhaustedError=isQuotaExhaustedError,exports.isAuthenticationError=isAuthenticationError,exports.readApiKeyFromSettings=readApiKeyFromSettings,exports.getForceEnabledSettingsPath=getForceEnabledSettingsPath,exports.startupCheck=startupCheck;const fs_1=__importDefault(require("fs")),path_1=__importDefault(require("path")),child_process_1=require("child_process"),session_1=require("./session"),utils_ok_1=require("utils-ok");function saveClientConfig(s){const e=`${s.getClientDir()}/config.json`;try{fs_1.default.writeFileSync(e,JSON.stringify(s.config,null,2),"utf-8")}catch(n){console.error(`[${(0,session_1.timestamp)()}] \u4FDD\u5B58 config.json \u5931\u8D25:`,n)}}function resetApiKeyCfg(s){const e=s.config.apiKeyCfg;if(!e)return;const n=new Date;e.resetTime=utils_ok_1.dateUtil.mm(n.getTime()).format("YYYY-MM-DD HH:mm:ss");let i=0;for(const l of e.claudeSettings)l.isValid||(l.isValid=!0,i++);i>0&&console.log(`[${(0,session_1.timestamp)()}] ${i} \u4E2A\u5DF2\u5931\u6548 Claude Setting \u91CD\u65B0\u6807\u8BB0\u4E3A\u6709\u6548`),saveClientConfig(s),console.log(`[${(0,session_1.timestamp)()}] apiKeyCfg \u5DF2\u91CD\u7F6E (\u6240\u6709 Claude Setting isValid=true)`)}function scheduleApiKeyCfgDailyReset(s){const e=()=>{const n=new Date,i=new Date(n.getFullYear(),n.getMonth(),n.getDate()+1,0,0,0,0),l=i.getTime()-n.getTime();setTimeout(()=>{console.log(`[${(0,session_1.timestamp)()}] \u5B9A\u65F6\u91CD\u7F6E apiKeyCfg (\u6BCF\u59290\u70B9)`),resetApiKeyCfg(s),e()},l),console.log(`[${(0,session_1.timestamp)()}] apiKeyCfg \u6BCF\u65E5\u91CD\u7F6E\u5DF2\u8C03\u5EA6\uFF0C\u4E0B\u6B21\u91CD\u7F6E: ${i.toISOString()}`)};e()}function settingLabel(s){return s.memo?s.memo:`...${s.apiKey.slice(-6)}`}function findSettingLabel(s,e){const n=s.find(i=>i.apiKey===e);return n?settingLabel(n):`...${e.slice(-6)}`}function rotateApiKey(s,e){const n=s.config.apiKeyCfg;if(!n)return null;for(const r of n.claudeSettings)if(r.apiKey===e&&r.isValid){r.isValid=!1;break}const i=n.claudeSettings.filter(r=>r.isValid).length,l=findSettingLabel(n.claudeSettings,e);return console.log(`[${(0,session_1.timestamp)()}] Claude Setting \u5DF2\u5931\u6548: ${l}, \u5269\u4F59\u6709\u6548: ${i}`),saveClientConfig(s),pickValidApiKey(s)}function pickValidApiKey(s){const e=s.config.apiKeyCfg;if(!e)return null;const n=e.claudeSettings.filter(i=>i.isValid);return n.length===0?null:n[Math.floor(Math.random()*n.length)]}function ensureSettingsWithApiKey(s,e){const n=path_1.default.join(s,".claude"),i=path_1.default.join(n,"settings-ding.json");let l={};if(fs_1.default.existsSync(i))try{l=JSON.parse(fs_1.default.readFileSync(i,"utf-8"))}catch{l={}}else{const g=path_1.default.join((0,session_1.getHomeDir)(),".cc-ding","settings-tpl.json");if(fs_1.default.existsSync(g))try{l=JSON.parse(fs_1.default.readFileSync(g,"utf-8")),console.log(`[${(0,session_1.timestamp)()}] \u4ECE\u6A21\u677F\u521B\u5EFA settings-ding.json: ${g}`)}catch{l={}}}l.env||(l.env={});let r=!1;l.env.ANTHROPIC_AUTH_TOKEN!==e.apiKey&&(l.env.ANTHROPIC_AUTH_TOKEN=e.apiKey,r=!0),e.baseUrl&&l.env.ANTHROPIC_BASE_URL!==e.baseUrl&&(l.env.ANTHROPIC_BASE_URL=e.baseUrl,r=!0),e.model&&l.env.ANTHROPIC_MODEL!==e.model&&(l.env.ANTHROPIC_MODEL=e.model,r=!0);const f=e.smallModel||e.model;return f&&l.env.CLAUDE_SMALL_FAST_MODEL!==f&&(l.env.CLAUDE_SMALL_FAST_MODEL=f,r=!0),r&&(fs_1.default.mkdirSync(n,{recursive:!0}),fs_1.default.writeFileSync(i,JSON.stringify(l,null,2),"utf-8"),console.log(`[${(0,session_1.timestamp)()}] \u5DF2\u5199\u5165 Claude \u914D\u7F6E\u5230 ${i} (${settingLabel(e)}, model: ${e.model}, smallModel: ${f})`)),i}function isQuotaExhaustedError(s){return!!(/Request\s+rejected.*429/i.test(s)||/429.*Request\s+rejected/i.test(s)||/429.*(?:超过.*上限|使用上限|配额|quota|capacity)/i.test(s)||/(?:超过.*上限|使用上限|配额|quota|capacity).*429/i.test(s))}function isAuthenticationError(s){return!!(/authentication_error/i.test(s)||/401.*(?:未授权|unauthorized|invalid\s*(?:key|token|api)|auth)/i.test(s)||/(?:未授权|unauthorized|invalid\s*(?:key|token|api)|auth).*401/i.test(s))}function readApiKeyFromSettings(s){const e=path_1.default.join(s,".claude","settings-ding.json");if(!fs_1.default.existsSync(e))return null;try{return JSON.parse(fs_1.default.readFileSync(e,"utf-8")).env?.ANTHROPIC_AUTH_TOKEN||null}catch{return null}}function getForceEnabledSettingsPath(s){const e=path_1.default.join(s,".claude","settings-ding.json");if(!fs_1.default.existsSync(e))return null;try{const i=JSON.parse(fs_1.default.readFileSync(e,"utf-8")).env?.FORCE_ENABLE;return i!==void 0&&i!==!1&&i!==""?(console.log(`[${(0,session_1.timestamp)()}] \u68C0\u6D4B\u5230 settings-ding.json FORCE_ENABLE=${i}\uFF0C\u5F3A\u5236\u4F7F\u7528\u8BE5\u914D\u7F6E`),e):null}catch{return null}}function startupCheck(s){const e=[],n=s.config,i=s.getClientDir(),l=[{key:"clientSecret",label:"clientSecret (\u9489\u9489 Stream Client \u5BC6\u94A5)"},{key:"whiteUserList",label:"whiteUserList (\u767D\u540D\u5355\u7528\u6237)"},{key:"owner",label:"owner (\u673A\u5668\u4EBA owner)"}];for(const{key:t,label:a}of l){const o=n[t];o==null||o===""||Array.isArray(o)&&o.length===0?e.push({level:"FATAL",message:`config.json \u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5: ${a}`}):e.push({level:"PASS",message:`config.json ${a} \u2713`})}if(!Array.isArray(n.conversations))e.push({level:"FATAL",message:"conversations \u5E94\u4E3A\u6570\u7EC4\u6216\u5DF2\u914D\u7F6E"});else if(n.conversations.length===0)e.push({level:"PASS",message:"conversations \u4E3A\u7A7A\u6570\u7EC4\uFF0C\u53EF\u901A\u8FC7 /reg \u547D\u4EE4\u52A8\u6001\u6CE8\u518C"});else{const t=new Set;for(let a=0;a<n.conversations.length;a++){const o=n.conversations[a],c=`conversations[${a}]`;o.conversationId?t.has(o.conversationId)?e.push({level:"WARN",message:`${c} conversationId \u91CD\u590D: ${o.conversationId}`}):t.add(o.conversationId):e.push({level:"FATAL",message:`${c} \u7F3A\u5C11 conversationId`}),o.linkConversationId&&!n.conversations.some(u=>u.conversationId===o.linkConversationId)&&e.push({level:"WARN",message:`${c} linkConversationId "${o.linkConversationId}" \u672A\u5728 conversations \u4E2D\u627E\u5230`})}e.push({level:"PASS",message:`conversations \u5171 ${n.conversations.length} \u4E2A\u7FA4\u914D\u7F6E`})}if(n.apiKeyCfg){const t=n.apiKeyCfg;if(t.resetTime&&e.push({level:"PASS",message:`apiKeyCfg \u4E0A\u6B21\u91CD\u7F6E\u65F6\u95F4: ${t.resetTime}`}),!Array.isArray(t.claudeSettings))e.push({level:"WARN",message:"apiKeyCfg.claudeSettings \u4E0D\u662F\u6570\u7EC4\uFF0CAPI Key \u8F6E\u6362\u529F\u80FD\u4E0D\u53EF\u7528"});else if(t.claudeSettings.length===0)e.push({level:"WARN",message:"apiKeyCfg.claudeSettings \u4E3A\u7A7A\uFF0C\u65E0\u53EF\u7528 Key"});else{const a=new Set;for(let c=0;c<t.claudeSettings.length;c++){const u=t.claudeSettings[c],d=`apiKeyCfg.claudeSettings[${c}]`;u.apiKey?a.has(u.apiKey)?e.push({level:"WARN",message:`${d} apiKey \u91CD\u590D: ${settingLabel(u)}`}):a.add(u.apiKey):e.push({level:"FATAL",message:`${d} \u7F3A\u5C11 apiKey`}),u.baseUrl||e.push({level:"WARN",message:`${d} \u7F3A\u5C11 baseUrl`}),u.model||e.push({level:"WARN",message:`${d} \u7F3A\u5C11 model`}),typeof u.isValid!="boolean"&&e.push({level:"WARN",message:`${d} isValid \u7C7B\u578B\u5F02\u5E38: ${typeof u.isValid}`})}const o=t.claudeSettings.filter(c=>c.isValid).length;e.push({level:"PASS",message:`apiKeyCfg.claudeSettings \u5171 ${t.claudeSettings.length} \u9879\uFF0C\u6709\u6548 ${o}`})}}const r=path_1.default.join((0,session_1.getHomeDir)(),".cc-ding","settings-tpl.json");if(fs_1.default.existsSync(r))try{const t=JSON.parse(fs_1.default.readFileSync(r,"utf-8"));if(typeof t!="object"||t===null)e.push({level:"WARN",message:"settings-tpl.json \u6839\u5143\u7D20\u4E0D\u662F\u5BF9\u8C61"});else if(!t.env||typeof t.env!="object")e.push({level:"WARN",message:"settings-tpl.json \u7F3A\u5C11 env \u5B57\u6BB5\uFF0C\u521B\u5EFA settings-ding.json \u65F6\u5C06\u4E0D\u5305\u542B\u9884\u914D\u7F6E\u73AF\u5883\u53D8\u91CF"});else{const a=Object.keys(t.env);e.push({level:"PASS",message:`settings-tpl.json \u6709\u6548\uFF0Cenv \u5305\u542B: ${a.join(", ")||"(\u7A7A)"}`})}}catch(t){e.push({level:"WARN",message:`settings-tpl.json \u89E3\u6790\u5931\u8D25: ${t instanceof Error?t.message:t}`})}else e.push({level:"WARN",message:`settings-tpl.json \u4E0D\u5B58\u5728: ${r}\uFF0C\u521B\u5EFA settings-ding.json \u65F6\u5C06\u4F7F\u7528\u7A7A\u6A21\u677F`});try{(0,child_process_1.execSync)("which claude",{stdio:"pipe"}),e.push({level:"PASS",message:"claude \u547D\u4EE4\u53EF\u7528"})}catch{e.push({level:"FATAL",message:"claude \u547D\u4EE4\u4E0D\u53EF\u7528\uFF0C\u8BF7\u786E\u8BA4 Claude Code CLI \u5DF2\u5B89\u88C5"})}try{const t=path_1.default.join(i,".healthcheck");fs_1.default.writeFileSync(t,"ok","utf-8"),fs_1.default.unlinkSync(t),e.push({level:"PASS",message:`\u5DE5\u4F5C\u76EE\u5F55\u53EF\u5199: ${i}`})}catch(t){e.push({level:"FATAL",message:`\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u53EF\u5199: ${i} \u2014 ${t instanceof Error?t.message:t}`})}if(Array.isArray(n.conversations))for(const t of n.conversations){const a=s.getConversationDir(t.conversationId);try{fs_1.default.mkdirSync(a,{recursive:!0}),e.push({level:"PASS",message:`\u7FA4\u5DE5\u4F5C\u76EE\u5F55\u5DF2\u5C31\u7EEA: ${t.conversationTitle||t.conversationId}`})}catch(o){e.push({level:"WARN",message:`\u7FA4\u5DE5\u4F5C\u76EE\u5F55\u521B\u5EFA\u5931\u8D25: ${a} \u2014 ${o instanceof Error?o.message:o}`})}}console.log(`
|
|
2
|
-
[${(0,
|
|
3
|
-
`),
|
|
1
|
+
"use strict";var C=exports&&exports.__importDefault||function(s){return s&&s.__esModule?s:{default:s}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.saveClientConfig=$,exports.resetApiKeyCfg=K,exports.scheduleApiKeyCfgDailyReset=R,exports.settingLabel=A,exports.rotateApiKey=L,exports.pickValidApiKey=T,exports.ensureSettingsWithApiKey=P,exports.isQuotaExhaustedError=E,exports.isAuthenticationError=b,exports.readApiKeyFromSettings=k,exports.getForceEnabledSettingsPath=D,exports.startupCheck=O;const r=C(require("fs")),d=C(require("path")),c=require("./session"),j=require("utils-ok"),v=require("./secrets"),S=require("./platform");function $(s){const e=`${s.getClientDir()}/config.json`;try{r.default.writeFileSync(e,JSON.stringify(s.config,null,2),{encoding:"utf-8",mode:(0,S.isWindows)()?void 0:384}),(0,S.isWindows)()||r.default.chmodSync(e,384)}catch(n){console.error(`[${(0,c.timestamp)()}] \u4FDD\u5B58 config.json \u5931\u8D25:`,n)}}function K(s){const e=s.config.apiKeyCfg;if(!e)return;const n=new Date;e.resetTime=j.dateUtil.mm(n.getTime()).format("YYYY-MM-DD HH:mm:ss");let i=0;for(const o of e.claudeSettings)o.isValid||(o.isValid=!0,i++);i>0&&console.log(`[${(0,c.timestamp)()}] ${i} \u4E2A\u5DF2\u5931\u6548 Claude Setting \u91CD\u65B0\u6807\u8BB0\u4E3A\u6709\u6548`),$(s),console.log(`[${(0,c.timestamp)()}] apiKeyCfg \u5DF2\u91CD\u7F6E (\u6240\u6709 Claude Setting isValid=true)`)}function R(s){const e=()=>{const n=new Date,i=new Date(n.getFullYear(),n.getMonth(),n.getDate()+1,0,0,0,0),o=i.getTime()-n.getTime();setTimeout(()=>{console.log(`[${(0,c.timestamp)()}] \u5B9A\u65F6\u91CD\u7F6E apiKeyCfg (\u6BCF\u59290\u70B9)`),K(s),e()},o),console.log(`[${(0,c.timestamp)()}] apiKeyCfg \u6BCF\u65E5\u91CD\u7F6E\u5DF2\u8C03\u5EA6\uFF0C\u4E0B\u6B21\u91CD\u7F6E: ${i.toISOString()}`)};e()}function A(s){return s.memo?s.memo:`...${s.apiKey.slice(-6)}`}function F(s,e){const n=s.find(i=>(0,v.resolveSecret)(i.apiKey)===(0,v.resolveSecret)(e));return n?A(n):`...${e.slice(-6)}`}function L(s,e){const n=s.config.apiKeyCfg;if(!n)return null;for(const u of n.claudeSettings)if((0,v.resolveSecret)(u.apiKey)===(0,v.resolveSecret)(e)&&u.isValid){u.isValid=!1;break}const i=n.claudeSettings.filter(u=>u.isValid).length,o=F(n.claudeSettings,e);return console.log(`[${(0,c.timestamp)()}] Claude Setting \u5DF2\u5931\u6548: ${o}, \u5269\u4F59\u6709\u6548: ${i}`),$(s),T(s)}function T(s){const e=s.config.apiKeyCfg;if(!e)return null;const n=e.claudeSettings.filter(i=>i.isValid);return n.length===0?null:n[Math.floor(Math.random()*n.length)]}function P(s,e){const n=d.default.join(s,".claude"),i=d.default.join(n,"settings-ding.json");let o={};if(r.default.existsSync(i))try{o=JSON.parse(r.default.readFileSync(i,"utf-8"))}catch{o={}}else{const h=d.default.join((0,c.getHomeDir)(),".cc-ding","settings-tpl.json");if(r.default.existsSync(h))try{o=JSON.parse(r.default.readFileSync(h,"utf-8")),console.log(`[${(0,c.timestamp)()}] \u4ECE\u6A21\u677F\u521B\u5EFA settings-ding.json: ${h}`)}catch{o={}}}o.env||(o.env={});let u=!1;const p=(0,v.resolveSecret)(e.apiKey);o.env.ANTHROPIC_AUTH_TOKEN!==p&&(o.env.ANTHROPIC_AUTH_TOKEN=p,u=!0),e.baseUrl&&o.env.ANTHROPIC_BASE_URL!==e.baseUrl&&(o.env.ANTHROPIC_BASE_URL=e.baseUrl,u=!0),e.model&&o.env.ANTHROPIC_MODEL!==e.model&&(o.env.ANTHROPIC_MODEL=e.model,u=!0);const m=e.smallModel||e.model;return m&&o.env.CLAUDE_SMALL_FAST_MODEL!==m&&(o.env.CLAUDE_SMALL_FAST_MODEL=m,u=!0),u&&(r.default.mkdirSync(n,{recursive:!0}),r.default.writeFileSync(i,JSON.stringify(o,null,2),{encoding:"utf-8",mode:(0,S.isWindows)()?void 0:384}),(0,S.isWindows)()||r.default.chmodSync(i,384),console.log(`[${(0,c.timestamp)()}] \u5DF2\u5199\u5165 Claude \u914D\u7F6E\u5230 ${i} (${A(e)}, model: ${e.model}, smallModel: ${m})`)),i}function E(s){return!!(/Request\s+rejected.*429/i.test(s)||/429.*Request\s+rejected/i.test(s)||/429.*(?:超过.*上限|使用上限|配额|quota|capacity)/i.test(s)||/(?:超过.*上限|使用上限|配额|quota|capacity).*429/i.test(s))}function b(s){return!!(/authentication_error/i.test(s)||/401.*(?:未授权|unauthorized|invalid\s*(?:key|token|api)|auth)/i.test(s)||/(?:未授权|unauthorized|invalid\s*(?:key|token|api)|auth).*401/i.test(s))}function k(s){const e=d.default.join(s,".claude","settings-ding.json");if(!r.default.existsSync(e))return null;try{return JSON.parse(r.default.readFileSync(e,"utf-8")).env?.ANTHROPIC_AUTH_TOKEN||null}catch{return null}}function D(s){const e=d.default.join(s,".claude","settings-ding.json");if(!r.default.existsSync(e))return null;try{const i=JSON.parse(r.default.readFileSync(e,"utf-8")).env?.FORCE_ENABLE;return i!==void 0&&i!==!1&&i!==""?(console.log(`[${(0,c.timestamp)()}] \u68C0\u6D4B\u5230 settings-ding.json FORCE_ENABLE=${i}\uFF0C\u5F3A\u5236\u4F7F\u7528\u8BE5\u914D\u7F6E`),e):null}catch{return null}}function O(s){const e=[],n=s.config,i=s.getClientDir(),o=[{key:"clientSecret",label:"clientSecret (\u9489\u9489 Stream Client \u5BC6\u94A5)"},{key:"whiteUserList",label:"whiteUserList (\u767D\u540D\u5355\u7528\u6237)"},{key:"owner",label:"owner (\u673A\u5668\u4EBA owner)"}];for(const{key:t,label:l}of o){const a=n[t];a==null||a===""||Array.isArray(a)&&a.length===0?e.push({level:"FATAL",message:`config.json \u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5: ${l}`}):e.push({level:"PASS",message:`config.json ${l} \u2713`})}if(!Array.isArray(n.conversations))e.push({level:"FATAL",message:"conversations \u5E94\u4E3A\u6570\u7EC4\u6216\u5DF2\u914D\u7F6E"});else if(n.conversations.length===0)e.push({level:"PASS",message:"conversations \u4E3A\u7A7A\u6570\u7EC4\uFF0C\u53EF\u901A\u8FC7 /reg \u547D\u4EE4\u52A8\u6001\u6CE8\u518C"});else{const t=new Set;for(let l=0;l<n.conversations.length;l++){const a=n.conversations[l],f=`conversations[${l}]`;a.conversationId?t.has(a.conversationId)?e.push({level:"WARN",message:`${f} conversationId \u91CD\u590D: ${a.conversationId}`}):t.add(a.conversationId):e.push({level:"FATAL",message:`${f} \u7F3A\u5C11 conversationId`}),a.linkConversationId&&!n.conversations.some(g=>g.conversationId===a.linkConversationId)&&e.push({level:"WARN",message:`${f} linkConversationId "${a.linkConversationId}" \u672A\u5728 conversations \u4E2D\u627E\u5230`})}e.push({level:"PASS",message:`conversations \u5171 ${n.conversations.length} \u4E2A\u7FA4\u914D\u7F6E`})}for(const t of n.conversations||[])if(t.permissionMode==="bypassPermissions"){const l=t.conversationTitle||t.conversationId;e.push({level:"WARN",message:`\u4F1A\u8BDD "${l}" \u914D\u7F6E\u4E86 bypassPermissions\uFF0CClaude \u5C06\u8DF3\u8FC7\u6240\u6709\u6743\u9650\u786E\u8BA4\uFF0C\u8BF7\u786E\u8BA4\u8BE5\u7FA4\u6210\u5458\u53EF\u4FE1`})}if(!(0,S.isWindows)()){const t=d.default.join(i,"config.json");try{const l=r.default.statSync(t).mode&511;l&63&&(r.default.chmodSync(t,384),e.push({level:"WARN",message:`config.json \u6743\u9650\u8FC7\u5BBD (${l.toString(8)})\uFF0C\u5DF2\u81EA\u52A8\u6536\u7D27\u4E3A 600`}))}catch{}}const u=[{value:n.clientSecret,label:"clientSecret"},{value:n.defaultDingToken,label:"defaultDingToken"},...(n.conversations||[]).map((t,l)=>({value:t.dingToken,label:`conversations[${l}].dingToken`})),...(n.apiKeyCfg?.claudeSettings||[]).map((t,l)=>({value:t.apiKey,label:`apiKeyCfg.claudeSettings[${l}].apiKey`}))];for(const{value:t,label:l}of u)(0,v.isEnvRef)(t)&&!(0,v.resolveSecret)(t)&&e.push({level:"FATAL",message:`${l} \u5F15\u7528\u7684\u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E: ${t}`});if(n.apiKeyCfg){const t=n.apiKeyCfg;if(t.resetTime&&e.push({level:"PASS",message:`apiKeyCfg \u4E0A\u6B21\u91CD\u7F6E\u65F6\u95F4: ${t.resetTime}`}),!Array.isArray(t.claudeSettings))e.push({level:"WARN",message:"apiKeyCfg.claudeSettings \u4E0D\u662F\u6570\u7EC4\uFF0CAPI Key \u8F6E\u6362\u529F\u80FD\u4E0D\u53EF\u7528"});else if(t.claudeSettings.length===0)e.push({level:"WARN",message:"apiKeyCfg.claudeSettings \u4E3A\u7A7A\uFF0C\u65E0\u53EF\u7528 Key"});else{const l=new Set;for(let f=0;f<t.claudeSettings.length;f++){const g=t.claudeSettings[f],y=`apiKeyCfg.claudeSettings[${f}]`;g.apiKey?l.has(g.apiKey)?e.push({level:"WARN",message:`${y} apiKey \u91CD\u590D: ${A(g)}`}):l.add(g.apiKey):e.push({level:"FATAL",message:`${y} \u7F3A\u5C11 apiKey`}),g.baseUrl||e.push({level:"WARN",message:`${y} \u7F3A\u5C11 baseUrl`}),g.model||e.push({level:"WARN",message:`${y} \u7F3A\u5C11 model`}),typeof g.isValid!="boolean"&&e.push({level:"WARN",message:`${y} isValid \u7C7B\u578B\u5F02\u5E38: ${typeof g.isValid}`})}const a=t.claudeSettings.filter(f=>f.isValid).length;e.push({level:"PASS",message:`apiKeyCfg.claudeSettings \u5171 ${t.claudeSettings.length} \u9879\uFF0C\u6709\u6548 ${a}`})}}const p=d.default.join((0,c.getHomeDir)(),".cc-ding","settings-tpl.json");if(r.default.existsSync(p))try{const t=JSON.parse(r.default.readFileSync(p,"utf-8"));if(typeof t!="object"||t===null)e.push({level:"WARN",message:"settings-tpl.json \u6839\u5143\u7D20\u4E0D\u662F\u5BF9\u8C61"});else if(!t.env||typeof t.env!="object")e.push({level:"WARN",message:"settings-tpl.json \u7F3A\u5C11 env \u5B57\u6BB5\uFF0C\u521B\u5EFA settings-ding.json \u65F6\u5C06\u4E0D\u5305\u542B\u9884\u914D\u7F6E\u73AF\u5883\u53D8\u91CF"});else{const l=Object.keys(t.env);e.push({level:"PASS",message:`settings-tpl.json \u6709\u6548\uFF0Cenv \u5305\u542B: ${l.join(", ")||"(\u7A7A)"}`})}}catch(t){e.push({level:"WARN",message:`settings-tpl.json \u89E3\u6790\u5931\u8D25: ${t instanceof Error?t.message:t}`})}else e.push({level:"WARN",message:`settings-tpl.json \u4E0D\u5B58\u5728: ${p}\uFF0C\u521B\u5EFA settings-ding.json \u65F6\u5C06\u4F7F\u7528\u7A7A\u6A21\u677F`});(0,S.commandExists)("claude")?e.push({level:"PASS",message:"claude \u547D\u4EE4\u53EF\u7528"}):e.push({level:"FATAL",message:"claude \u547D\u4EE4\u4E0D\u53EF\u7528\uFF0C\u8BF7\u786E\u8BA4 Claude Code CLI \u5DF2\u5B89\u88C5"});try{const t=d.default.join(i,".healthcheck");r.default.writeFileSync(t,"ok","utf-8"),r.default.unlinkSync(t),e.push({level:"PASS",message:`\u5DE5\u4F5C\u76EE\u5F55\u53EF\u5199: ${i}`})}catch(t){e.push({level:"FATAL",message:`\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u53EF\u5199: ${i} \u2014 ${t instanceof Error?t.message:t}`})}if(Array.isArray(n.conversations))for(const t of n.conversations){const l=s.getConversationDir(t.conversationId);try{r.default.mkdirSync(l,{recursive:!0}),e.push({level:"PASS",message:`\u7FA4\u5DE5\u4F5C\u76EE\u5F55\u5DF2\u5C31\u7EEA: ${t.conversationTitle||t.conversationId}`})}catch(a){e.push({level:"WARN",message:`\u7FA4\u5DE5\u4F5C\u76EE\u5F55\u521B\u5EFA\u5931\u8D25: ${l} \u2014 ${a instanceof Error?a.message:a}`})}}console.log(`
|
|
2
|
+
[${(0,c.timestamp)()}] ========== \u542F\u52A8\u81EA\u68C0 ==========`);const m=e.some(t=>t.level==="FATAL");for(const t of e){const l=t.level==="PASS"?"\u2713":t.level==="FATAL"?"\u2717":"\u26A0",a=t.level==="PASS"?t.message:t.level==="FATAL"?`[FATAL] ${t.message}`:`[WARN] ${t.message}`;console.log(` ${l} ${a}`)}const h=e.filter(t=>t.level==="PASS").length,N=e.filter(t=>t.level==="WARN").length,_=e.filter(t=>t.level==="FATAL").length;console.log(`[${(0,c.timestamp)()}] \u81EA\u68C0\u5B8C\u6210: ${h} \u901A\u8FC7, ${N} \u8B66\u544A, ${_} \u81F4\u547D`),console.log(`[${(0,c.timestamp)()}] ==============================
|
|
3
|
+
`),m&&(console.error(`[${(0,c.timestamp)()}] \u542F\u52A8\u81EA\u68C0\u53D1\u73B0\u81F4\u547D\u9519\u8BEF\uFF0C\u8FDB\u7A0B\u9000\u51FA`),process.exit(1))}
|