chatccc 0.2.43 → 0.2.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.
package/src/index.ts CHANGED
@@ -608,7 +608,8 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
608
608
  logTrace(tid, "BRANCH", { cmd: "/sessions" });
609
609
  const allSessions = await getAllSessionsStatus();
610
610
  const now = Date.now();
611
- const cardData = allSessions.map(s => ({
611
+ const others = allSessions.filter(s => s.chatId !== chatId);
612
+ const cardData = others.map(s => ({
612
613
  sessionId: s.sessionId,
613
614
  chatName: s.chatName,
614
615
  active: s.active,
@@ -619,8 +620,8 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
619
620
  }));
620
621
  const card = buildSessionsCard(cardData);
621
622
  const ok = await sendRawCard(freshToken, chatId, card);
622
- console.log(`[${ts()}] [SESSIONS] card sent, ok=${ok}, count=${allSessions.length}`);
623
- logTrace(tid, "DONE", { outcome: "sessions", ok, count: allSessions.length });
623
+ console.log(`[${ts()}] [SESSIONS] card sent, ok=${ok}, count=${others.length}`);
624
+ logTrace(tid, "DONE", { outcome: "sessions", ok, count: others.length });
624
625
  return;
625
626
  }
626
627
 
@@ -702,17 +703,22 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
702
703
  const index = parseInt(sessionMatch[1], 10) - 1;
703
704
  logTrace(tid, "BRANCH", { cmd: "/session", index: index + 1 });
704
705
  const allSessions = await getAllSessionsStatus();
705
- if (allSessions.length === 0) {
706
+ // buildSessionsCard 保持一致的排序:Claude Code → Cursor → Codex,组内保持 updatedAt 降序
707
+ const claudeOrdered = allSessions.filter(s => s.tool !== "cursor" && s.tool !== "codex");
708
+ const cursorOrdered = allSessions.filter(s => s.tool === "cursor");
709
+ const codexOrdered = allSessions.filter(s => s.tool === "codex");
710
+ const ordered = [...claudeOrdered, ...cursorOrdered, ...codexOrdered].filter(s => s.chatId !== chatId);
711
+ if (ordered.length === 0) {
706
712
  await sendCardReply(freshToken, chatId, "/session", "暂无历史会话。", "yellow");
707
713
  logTrace(tid, "DONE", { outcome: "session_no_sessions" });
708
714
  return;
709
715
  }
710
- if (index < 0 || index >= allSessions.length) {
711
- await sendCardReply(freshToken, chatId, "/session", `序号超出范围,当前共 ${allSessions.length} 个会话。`, "yellow");
712
- logTrace(tid, "DONE", { outcome: "session_out_of_range", index: index + 1, total: allSessions.length });
716
+ if (index < 0 || index >= ordered.length) {
717
+ await sendCardReply(freshToken, chatId, "/session", `序号超出范围,当前共 ${ordered.length} 个会话。`, "yellow");
718
+ logTrace(tid, "DONE", { outcome: "session_out_of_range", index: index + 1, total: ordered.length });
713
719
  return;
714
720
  }
715
- const target = allSessions[index];
721
+ const target = ordered[index];
716
722
 
717
723
  const existing2 = chatSessionMap.get(chatId);
718
724
  if (existing2) {
@@ -736,8 +742,9 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
736
742
  }
737
743
 
738
744
  const descPrefix2 = sessionPrefixForTool(target.tool);
739
- const newName2 = sessionChatName("新会话", cwd2);
745
+ const newName2 = target.chatName || sessionChatName("新会话", cwd2);
740
746
  await updateChatInfo(freshToken, chatId, newName2, `${descPrefix2} ${target.sessionId}`);
747
+ console.log(`[${ts()}] [SESSION] Switched to session ${target.sessionId} (#${index + 1}), name="${newName2}"`);
741
748
 
742
749
  sessionInfoMap.set(chatId, {
743
750
  sessionId: target.sessionId,
@@ -767,7 +774,6 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
767
774
  "green"
768
775
  );
769
776
 
770
- console.log(`[${ts()}] [SESSION] Switched to session ${target.sessionId} (#${index + 1})`);
771
777
  logTrace(tid, "DONE", { outcome: "session_switch", sessionId: target.sessionId, index: index + 1, cwd: cwd2 });
772
778
  return;
773
779
  }
@@ -839,7 +845,7 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
839
845
  console.log(`[${ts()}] [BLOCKED] allowInterrupt=false, ignoring message during generation. Hint sent to user.`);
840
846
  await sendCardReply(
841
847
  freshToken, chatId, "生成中",
842
- "AI 正在生成回复中,请先点击卡片上的「停止」按钮停止当前生成后,再发送新消息。",
848
+ "AI 正在生成回复中,Agent 不会遗忘当前进度,停止后仍可从断点继续。\n请先点击卡片上的「停止」按钮,再发送新消息。",
843
849
  "yellow"
844
850
  );
845
851
  return;
package/src/shared.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  import { execSync } from "node:child_process";
2
- import {
3
- appendFileSync,
4
- existsSync,
5
- mkdirSync,
6
- readFileSync,
7
- unlinkSync,
8
- writeFileSync,
9
- } from "node:fs";
10
- import { createServer } from "node:http";
11
- import { homedir } from "node:os";
12
- import { join } from "node:path";
13
- import { inspect } from "node:util";
14
- import { WebSocketServer, WebSocket } from "ws";
2
+ import {
3
+ appendFileSync,
4
+ existsSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ unlinkSync,
8
+ writeFileSync,
9
+ } from "node:fs";
10
+ import { createServer } from "node:http";
11
+ import { homedir } from "node:os";
12
+ import { join } from "node:path";
13
+ import { inspect } from "node:util";
14
+ import { WebSocketServer, WebSocket } from "ws";
15
15
 
16
16
  import { printServiceDidNotStart } from "./exit-banner.ts";
17
17
 
@@ -355,56 +355,56 @@ export function installCrashLogging(opts: CrashHandlersOptions = {}): InstallCra
355
355
  // 文件日志:同时输出到控制台和日志文件
356
356
  // ---------------------------------------------------------------------------
357
357
 
358
- export function setupFileLogging(logDir: string, prefix: string): { logPath: string; flush: () => void } {
359
- mkdirSync(logDir, { recursive: true });
360
- const ts = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
361
- const logPath = join(logDir, `${prefix}-${ts}.log`);
362
- writeFileSync(logPath, "", { flag: "a", encoding: "utf8" });
363
- const origConsoleLog = console.log.bind(console);
364
- const origConsoleError = console.error.bind(console);
365
- const formatArg = (arg: unknown): string => {
366
- if (typeof arg === "string") return arg;
367
- if (arg instanceof Error) return arg.stack ?? arg.message;
368
- return inspect(arg, { depth: 6, breakLength: Infinity, maxArrayLength: 200 });
369
- };
370
- const writeLine = (level: string, args: unknown[]) => {
371
- try {
372
- const line = args.map(formatArg).join(" ");
373
- appendFileSync(logPath, `[${new Date().toISOString()}] [${level}] ${line}\n`, "utf8");
374
- } catch (err) {
375
- try {
376
- appendStartupTrace("fileLog write failed", { level, error: err instanceof Error ? err.message : String(err) });
377
- } catch {
378
- // 日志系统自身不能影响主流程
379
- }
380
- }
381
- };
382
- console.log = (...args: unknown[]) => {
383
- writeLine("LOG", args);
384
- try {
385
- origConsoleLog(...args);
386
- } catch {
387
- // 控制台输出失败也不能拖垮服务
388
- }
389
- };
390
- console.error = (...args: unknown[]) => {
391
- writeLine("ERR", args);
392
- try {
393
- origConsoleError(...args);
394
- } catch {
395
- // 控制台输出失败也不能拖垮服务
396
- }
397
- };
398
- const flush = () => {
399
- try {
400
- appendFileSync(logPath, "", "utf8");
401
- } catch (err) {
402
- appendStartupTrace("fileLog flush failed", { error: err instanceof Error ? err.message : String(err) });
403
- }
404
- };
405
- origConsoleLog(`Log file: ${logPath}`);
406
- return { logPath, flush };
407
- }
358
+ export function setupFileLogging(logDir: string, prefix: string): { logPath: string; flush: () => void } {
359
+ mkdirSync(logDir, { recursive: true });
360
+ const ts = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
361
+ const logPath = join(logDir, `${prefix}-${ts}.log`);
362
+ writeFileSync(logPath, "", { flag: "a", encoding: "utf8" });
363
+ const origConsoleLog = console.log.bind(console);
364
+ const origConsoleError = console.error.bind(console);
365
+ const formatArg = (arg: unknown): string => {
366
+ if (typeof arg === "string") return arg;
367
+ if (arg instanceof Error) return arg.stack ?? arg.message;
368
+ return inspect(arg, { depth: 6, breakLength: Infinity, maxArrayLength: 200 });
369
+ };
370
+ const writeLine = (level: string, args: unknown[]) => {
371
+ try {
372
+ const line = args.map(formatArg).join(" ");
373
+ appendFileSync(logPath, `[${new Date().toISOString()}] [${level}] ${line}\n`, "utf8");
374
+ } catch (err) {
375
+ try {
376
+ appendStartupTrace("fileLog write failed", { level, error: err instanceof Error ? err.message : String(err) });
377
+ } catch {
378
+ // 日志系统自身不能影响主流程
379
+ }
380
+ }
381
+ };
382
+ console.log = (...args: unknown[]) => {
383
+ writeLine("LOG", args);
384
+ try {
385
+ origConsoleLog(...args);
386
+ } catch {
387
+ // 控制台输出失败也不能拖垮服务
388
+ }
389
+ };
390
+ console.error = (...args: unknown[]) => {
391
+ writeLine("ERR", args);
392
+ try {
393
+ origConsoleError(...args);
394
+ } catch {
395
+ // 控制台输出失败也不能拖垮服务
396
+ }
397
+ };
398
+ const flush = () => {
399
+ try {
400
+ appendFileSync(logPath, "", "utf8");
401
+ } catch (err) {
402
+ appendStartupTrace("fileLog flush failed", { error: err instanceof Error ? err.message : String(err) });
403
+ }
404
+ };
405
+ origConsoleLog(`Log file: ${logPath}`);
406
+ return { logPath, flush };
407
+ }
408
408
 
409
409
  // ---------------------------------------------------------------------------
410
410
  // 本地 WebSocket 中继服务器(同一端口、多客户端广播)