chatccc 0.2.161 → 0.2.163
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 +1 -0
- package/package.json +1 -1
- package/src/__tests__/card-plain-text.test.ts +1 -0
- package/src/__tests__/cards.test.ts +1 -1
- package/src/cards.ts +3 -0
- package/src/index.ts +1 -1
- package/src/orchestrator.ts +71 -3
package/README.md
CHANGED
|
@@ -313,6 +313,7 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
|
|
|
313
313
|
| `/plan <内容>` | 只读计划模式:仅允许读文件和 stop-stuck-loop 请求,不执行任何写操作 |
|
|
314
314
|
| `/ask <内容>` | 只读问答模式:与 /plan 相同,仅允许读文件和 stop-stuck-loop 请求 |
|
|
315
315
|
| `/restart` | 重启机器人进程 |
|
|
316
|
+
| `/updateg` | 更新 npm 全局包并重启(仅限 `npm install -g chatccc` 安装的全局进程) |
|
|
316
317
|
|
|
317
318
|
> **模型切换**:`/model` 查看可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。当前仅 Claude Code 支持模型切换(需同时填写 `claude.model` 和 `claude.subagentModel`),Cursor / Codex 切换正在开发中。
|
|
318
319
|
|
package/package.json
CHANGED
|
@@ -156,7 +156,7 @@ describe("buildHelpCard", () => {
|
|
|
156
156
|
const parsed = JSON.parse(card);
|
|
157
157
|
const action = parsed.elements[2];
|
|
158
158
|
expect(action.tag).toBe("action");
|
|
159
|
-
expect(action.actions).toHaveLength(
|
|
159
|
+
expect(action.actions).toHaveLength(7);
|
|
160
160
|
});
|
|
161
161
|
});
|
|
162
162
|
|
package/src/cards.ts
CHANGED
|
@@ -135,6 +135,8 @@ export function buildHelpCard(
|
|
|
135
135
|
"发送 **/newh** 重置当前会话(沿用当前工作目录,不切换)",
|
|
136
136
|
"发送 **/plan** 以规划模式提问(只读,不执行写操作)",
|
|
137
137
|
"发送 **/ask** 以问答模式提问(只读,不执行写操作)",
|
|
138
|
+
"发送 **/restart** 重启 ChatCCC 进程",
|
|
139
|
+
"发送 **/updateg** 更新并重启(仅 npm 全局安装可用)",
|
|
138
140
|
].join("\n");
|
|
139
141
|
return JSON.stringify({
|
|
140
142
|
config: { wide_screen_mode: true },
|
|
@@ -148,6 +150,7 @@ export function buildHelpCard(
|
|
|
148
150
|
{ text: "新建 Cursor 会话(/new cursor)", value: JSON.stringify({ cmd: "new cursor" }), type: "primary" },
|
|
149
151
|
{ text: "新建 Codex 会话(/new codex)", value: JSON.stringify({ cmd: "new codex" }), type: "primary" },
|
|
150
152
|
{ text: "重启 ChatCCC(/restart)", value: JSON.stringify({ cmd: "restart" }), type: "danger" },
|
|
153
|
+
{ text: "更新并重启(/updateg)", value: JSON.stringify({ cmd: "updateg" }), type: "danger" },
|
|
151
154
|
{ text: "切换工作路径(/cd)", value: JSON.stringify({ cmd: "cd" }), type: "default" },
|
|
152
155
|
]),
|
|
153
156
|
],
|
package/src/index.ts
CHANGED
|
@@ -213,7 +213,7 @@ function parseCardAction(data: unknown): CardActionResult | null {
|
|
|
213
213
|
}
|
|
214
214
|
if (!cmd) return null;
|
|
215
215
|
|
|
216
|
-
const CMD_MAP: Record<string, string> = { stop: "/stop", cancel: "/cancel", new: "/new", "new claude": "/new claude", "new cursor": "/new cursor", "new codex": "/new codex", restart: "/restart", state: "/state", cd: "/cd", sessions: "/sessions", newh: "/newh" };
|
|
216
|
+
const CMD_MAP: Record<string, string> = { stop: "/stop", cancel: "/cancel", new: "/new", "new claude": "/new claude", "new cursor": "/new cursor", "new codex": "/new codex", restart: "/restart", updateg: "/updateg", state: "/state", cd: "/cd", sessions: "/sessions", newh: "/newh" };
|
|
217
217
|
let text = CMD_MAP[cmd] ?? "";
|
|
218
218
|
if (cmd === "cd" && typeof action.value === "object" && action.value !== null) {
|
|
219
219
|
const path = (action.value as Record<string, string>).path;
|
package/src/orchestrator.ts
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
* 所有 IM 平台操作通过 PlatformAdapter 接口注入,不直接依赖 feishu-platform.ts。
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { spawn } from "node:child_process";
|
|
8
|
+
import { execSync, spawn } from "node:child_process";
|
|
9
9
|
import { readdir, stat } from "node:fs/promises";
|
|
10
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { resolve, dirname } from "node:path";
|
|
10
|
+
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join, resolve, dirname } from "node:path";
|
|
12
|
+
import { homedir } from "node:os";
|
|
12
13
|
|
|
13
14
|
import { makeTraceId, logTrace } from "./trace.ts";
|
|
14
15
|
import { appendStartupTrace } from "./shared.ts";
|
|
@@ -159,6 +160,58 @@ async function cleanupNonWechatP2pBinding(
|
|
|
159
160
|
}
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
/** 检测当前进程是否从 npm 全局安装启动 */
|
|
164
|
+
function isRunningFromGlobalNpm(): boolean {
|
|
165
|
+
try {
|
|
166
|
+
const globalRoot = execSync("npm root -g", { encoding: "utf8", timeout: 5000, windowsHide: true }).trim();
|
|
167
|
+
return resolve(PROJECT_ROOT).startsWith(resolve(globalRoot));
|
|
168
|
+
} catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const UPDATEG_LOG = join(homedir(), ".chatccc", "logs", "updateg-watcher.log");
|
|
174
|
+
|
|
175
|
+
function updLog(msg: string): void {
|
|
176
|
+
const ts = new Date().toISOString();
|
|
177
|
+
try { appendFileSync(UPDATEG_LOG, `${ts} [UPG-SYNC] ${msg}\n`, "utf-8"); } catch {}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** 同步更新 npm 全局包并重启(父进程存活等待,避免 systemd KillMode=control-group 杀 watcher) */
|
|
181
|
+
function syncUpdateAndRestart(): void {
|
|
182
|
+
updLog(`sync update start, pid=${process.pid}`);
|
|
183
|
+
|
|
184
|
+
// 1. npm update
|
|
185
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
186
|
+
try {
|
|
187
|
+
const out = execSync(`${npmCmd} update -g chatccc`, { encoding: "utf8", timeout: 120000, windowsHide: true });
|
|
188
|
+
updLog(`npm update OK: ${out.slice(0, 200)}`);
|
|
189
|
+
} catch (e) {
|
|
190
|
+
const errMsg = (e as Error).message;
|
|
191
|
+
updLog(`npm update failed: ${errMsg}, falling back to npm install`);
|
|
192
|
+
try {
|
|
193
|
+
const out2 = execSync(`${npmCmd} install -g chatccc@latest`, { encoding: "utf8", timeout: 120000, windowsHide: true });
|
|
194
|
+
updLog(`npm install fallback OK: ${out2.slice(0, 200)}`);
|
|
195
|
+
} catch (e2) {
|
|
196
|
+
updLog(`npm install fallback also failed: ${(e2 as Error).message}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 2. spawn new chatccc
|
|
201
|
+
const npmPrefix = process.env.NPM_PREFIX || "";
|
|
202
|
+
const binName = process.platform === "win32" ? "chatccc.cmd" : "chatccc";
|
|
203
|
+
const binPath = npmPrefix ? join(npmPrefix, binName) : "chatccc";
|
|
204
|
+
try {
|
|
205
|
+
const child = spawn(binPath, [], { detached: true, stdio: "ignore", shell: true });
|
|
206
|
+
child.unref();
|
|
207
|
+
updLog(`spawn new chatccc OK, childPid=${child.pid}, bin=${binPath}`);
|
|
208
|
+
} catch (e) {
|
|
209
|
+
updLog(`spawn new chatccc failed: ${(e as Error).message}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
updLog("sync update done, parent exiting in 2s");
|
|
213
|
+
}
|
|
214
|
+
|
|
162
215
|
// ---------------------------------------------------------------------------
|
|
163
216
|
// handleCommand — 平台无关的命令分发
|
|
164
217
|
// ---------------------------------------------------------------------------
|
|
@@ -212,6 +265,21 @@ export async function handleCommand(
|
|
|
212
265
|
return;
|
|
213
266
|
}
|
|
214
267
|
|
|
268
|
+
if (textLower === "/updateg") {
|
|
269
|
+
logTrace(tid, "BRANCH", { cmd: "/updateg" });
|
|
270
|
+
if (!isRunningFromGlobalNpm()) {
|
|
271
|
+
await platform.sendText(chatId, "当前进程非 npm 全局安装,无法使用 /updateg 更新。请通过 npm install -g chatccc 安装后使用。").catch(() => {});
|
|
272
|
+
logTrace(tid, "DONE", { outcome: "updateg_not_global" });
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
await platform.sendText(chatId, "正在更新并重启,请稍候...").catch(() => {});
|
|
276
|
+
logTrace(tid, "DONE", { outcome: "updateg" });
|
|
277
|
+
appendStartupTrace("updateg: sync update begin", { fromPid: process.pid });
|
|
278
|
+
syncUpdateAndRestart();
|
|
279
|
+
setTimeout(() => process.exit(0), 2000);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
215
283
|
if (textLower === "/cd" || textLower.startsWith("/cd ")) {
|
|
216
284
|
logTrace(tid, "BRANCH", {
|
|
217
285
|
cmd: "/cd",
|